做文创的网站,开发者模式怎么开启,国外网页模板,wordpress编辑小工具grant_type
client_credentials 客户端凭证password 密码模式 用于资源所有者密码凭据token 隐藏式 、 简化式 简化模式又称为隐式授权码模式#xff0c;它是授权码模式的一个简化版本authorization_code 授权码 A. 第三方程序向资源拥有者(用户)发送授权请求#xf…grant_type
client_credentials 客户端凭证password 密码模式 用于资源所有者密码凭据token 隐藏式 、 简化式 简化模式又称为隐式授权码模式它是授权码模式的一个简化版本authorization_code 授权码 A. 第三方程序向资源拥有者(用户)发送授权请求这个过程既可以通过客户端直接向用户请求也可以通过授权服务器作为中介来完成请求。注对于授权请求这个概念相当于用户登录应用程序可以直接显示一个登录页面也可以跳转到验证服务器的统一登录页面 B. 用户将授权相关信息“提交”给第三方程序在OAuth中有4种不同的权限授予方式每种方式需要的数据不同如基于用户密码的授权方式就需要用户名和密码。 C. 第三方程序将用户的授权信息提交到授权服务器请求一个Access Token。 D. 授权服务器验证完成用户的授权信息后将Access Token发放到第三方程序。 E. 第三方程序携带Access Token访问被保护的资源。 F. 资源服务器验证Access Token有效后将资源返回到第三方程序。 ● Authorization Code(授权码模式)该模式的核心是客户端通过一个授权码来向授权服务器申请Access Token。是一种基于重定向的授权模式授权服务器作为用户和第三方应用(Client)的中介当用户访问第三方应用是第三方应用跳转到授权服务器引导用户完成身份验证生成Authorization Code并转交到第三方应用以便于第三方应用根据这个授权码完成后续的Access Token获取。 ● Implicit简化模式简化模式是一种简化的授权码模式授权码模式在首次访问第三方应用时跳转到授权服务器进行身份验证返回授权码而简化模式在跳转到授权服务器后直接返回Access Token这种模式减少了获取Access Token的请求次数。 ● Resource Owner Password Credentials用户密码模式通过资源拥有者(用户)的用户名和密码来直接获取Access Token的一种方法这种方法要求第三方应用(Client)是高度可信任的并且其它授权方式不可用的情况下使用。 ● Client Credentials客户端模式该模式是通过第三方应用(Client)发送一个自己的凭证到授权服务器获得Access Token这种模式的使用要求该Client已经被授权服务器管理并限制其对被保护资源的访问范围。另外这种模式下Client应该就是一个资源拥有者(用户)如微服务程序。
》》》 四个模式中只有【客户端模式】不需要用户输入用户名和密码因为 客户端模式不是用户名义请求的是客户端本身名义请求的所以需要后台提供 client_id 和 client_secret, 根据这个两个去认证服务器【Authorization server】 获取access_token. 》》适用于没有前端的命令行应用即在命令行下请求令牌。一般用来提供给我们完全信任的服务器端服务。
如果是第一方应用自己开发的应用一般我们都认为要安全一些因为不会故意的去泄露访问resource的access token 所以一般第一方应用我们可以使用简单的【密码模式】, 这种节省了通过code去交换access token这一步骤实际上就是节省了一次网络请求来回直接通过用户名密码去获取access token。而第三方应用我们需要采用更加安全的 【授权码模式】和【简单模式】
【授权码模式】和【简单模式】、【密码模式】 都需要用户录入用户名和密码 但【授权码模式】和【简单模式】 是认证服务器【authorization server】提供的界面录入的【密码模式】是客户端提供的界面录入的 所以认证服务器提供的界面更加安全些
【简单模式】是没有授权码【code】和刷新token【refresh_code】 如果有人很容易的拿到code 或 refresh token那么就基本上可以随意随时的去访问你的resource了因为他可以不断的通过refresh token 去刷新access token。 而为什么第三方的SPA使用的是implicit flow 而第三方的Native App却使用的是authorization code flow 理论上第三方应用都应该使用【授权码模式】但是如果你仔细看下【简化模式】中是没有code 和 refresh token的而SPA应用(本质上是web需要通过浏览器的)更加容易去暴露code 和 refresh token 所以才在第三方的SPA应用中使用了【简单模式】而只给了access token
》》》
安装四个包 客户端模式 又称简化模式
客户端模式Client Credentials Grant指客户端以自己的名义而不是以用户的名义向服务提供商进行 授权。
适用于没有前端的命令行应用即在命令行下请求令牌。一般用来提供给我们完全信任的服务器端服务。 》》》》 用webapi 做案例 新建项目 【webapi】 》》》删除自动的Global.asax, 这个文件是程序的入口删除之后要创建一个 OWIN Startup 命名为 Startup。
using System;
using System.Threading.Tasks;
using Microsoft.Owin;
using Owin;
using System.Web.Http;
using WebApplication3.App_Start;
using Microsoft.Owin.Cors;[assembly: OwinStartup(typeof(WebApplication3.Startup))]namespace WebApplication3
{public class Startup{public void Configuration(IAppBuilder app){ HttpConfiguration configuration new HttpConfiguration();//注册Swagger//SwaggerConfig.Register(configuration);//注册WebAPIWebApiConfig.Register(configuration);//注册授权服务AuthorizationConfig.Register(app);//注册Json的数据展示格式JsonFormatConfig.Register(configuration);//跨域配置app.UseCors(CorsOptions.AllowAll);app.UseWebApi(configuration);}}
}
》》》 新建类 AuthorizationConfig using Microsoft.Owin.Security.OAuth;
using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using WebApplication3.Provider;namespace WebApplication3.App_Start
{public class AuthorizationConfig{public static void Register(Owin.IAppBuilder app){OAuthAuthorizationServerOptions options new OAuthAuthorizationServerOptions(){AllowInsecureHttp true,//允许http而非https访问TokenEndpointPath new Microsoft.Owin.PathString(value: /access_token),//Token 请求地址AccessTokenExpireTimeSpan TimeSpan.FromMinutes(30),//Token的过期时间Provider new OpenAuthorizationServerProvider(),//生成Token 配置RefreshTokenProvider new OpenRefreshTokenProvider(),//生成RefreshToken的配置};app.UseOAuthAuthorizationServer(options);app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());}}
}》》》新建类 JsonFormatConfig using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;namespace WebApplication3.App_Start
{public class JsonFormatConfig{public static void Register(HttpConfiguration configuration){configuration.Formatters.JsonFormatter.SerializerSettings new Newtonsoft.Json.JsonSerializerSettings(){ContractResolver new CamelCasePropertyNamesContractResolver(),//小驼峰命名DateFormatString yyyy-MM-dd HH:mm:ss //日期格式化};}}
}》》》新建文件夹Provider 》》》新建类 OpenAuthorizationServerProvider 生成 access_token
using Microsoft.Owin.Security.OAuth;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Threading.Tasks;
using System.Security.Claims;
using System.Security.Principal;namespace WebApplication3.Provider
{/// summary/// 授权服务器配置/// /summarypublic class OpenAuthorizationServerProvider:OAuthAuthorizationServerProvider{/// summary/// 验证客户端信息/// /summary/// param namecontext/param/// returns/returnspublic override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context){//如果是刷新token且不要验证。if (context.Parameters.Get(refresh_token) null){//获取clientIdClientSecretstring clientId, clientSecret;if (!context.TryGetBasicCredentials(out clientId, out clientSecret)){context.TryGetFormCredentials(out clientId, out clientSecret);}//对客户端Id和客户端密码进行校验 是与数据库进行比对if (clientId zen clientSecret 123456){//通过客户端认证context.Validated(clientId);}else{context.Rejected();}}else{ // 通过客户端认证context.Validated();}return base.ValidateClientAuthentication(context);}/// summary/// 生成客户端模式Access_Token/// 还需要将对应的客户端信息存储在web中/// /summary/// param namecontext/param/// returns/returnspublic override Task GrantClientCredentials(OAuthGrantClientCredentialsContext context){//以下即为认证成功//var identity new ClaimsIdentity(context.Options.AuthenticationType);ClaimsIdentity identity new GenericIdentity( name: context.ClientId, type: OAuthDefaults.AuthenticationType);//通过查数据库得到一些用户的信息int userid 13;string role 管理员;string scope 权限1,权限2; identity.AddClaim(new Claim(userid, userid.ToString()));identity.AddClaim(new Claim(role, role));identity.AddClaim(new Claim(scope, scope));context.Validated(identity);return base.GrantClientCredentials(context);}}
}》》》新建类 OpenRefreshTokenProvider 刷新token using Microsoft.Owin.Security.Infrastructure;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Web;namespace WebApplication3.Provider
{/// summary/// 刷新Token配置/// /summarypublic class OpenRefreshTokenProvider:AuthenticationTokenProvider{private static ConcurrentDictionarystring, string _refreshTokens new ConcurrentDictionarystring, string();/// summary/// 生成Refresh_token/// /summary/// param namecontext/parampublic override void Create(AuthenticationTokenCreateContext context){context.Ticket.Properties.IssuedUtc DateTime.UtcNow;context.Ticket.Properties.ExpiresUtc DateTime.UtcNow.AddDays(30);context.SetToken(tokenValue:Guid.NewGuid().ToString(format:N)Guid.NewGuid().ToString(format:N));_refreshTokens[context.Token] context.SerializeTicket();//base.Create(context); }/// summary/// 使用Refresh_token 请求Access_Token/// /summary/// param namecontext/parampublic override void Receive(AuthenticationTokenReceiveContext context){string value;if (_refreshTokens.TryRemove(context.Token,out value)){context.DeserializeTicket(value);}base.Receive(context);}}
}》》》 新建控制器
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Web;
using System.Web.Http;namespace WebApplication3.Controllers
{public class HomeController : ApiController{// GET: Home[Authorize]public string Get(){var clientId HttpContext.Current.User.Identity.Name;Dictionarystring,string lst new Dictionarystring,string();foreach (Claim item in (this.User.Identity as ClaimsIdentity).Claims){lst.Add(item.Type,item.Value);}return 我是Get方法;}// GET: Homepublic string Get(int id){return $这是参数为{id}的Get方法;}}
}》》》测试 用postman 》》客户端凭证 走 GrantClientCredentials方法 ValidateAuthorizeRequest 》》》授权码验证 ValidateClientAuthentication 》》》客户端模式验证 ValidateClientRedirectUri ValidateTokenRequest 》》》验证令牌请求 简化模式、隐藏式模式
密码模式Password Grant
用户将用户名和密码发送给第三方应用程序第三方应用程序直接向授权服务器请求访问令牌。
如果你高度信任某个应用RFC 6749 也允许用户把用户名和密码直接告诉该应用。该应用就使用你的密码申请令牌这种方式称为密码式password。
在这种模式中用户必须把自己的密码给客户端但是客户端不得储存密码。这通常用在用户对客户端高度信任的情况下比如客户端是操作系统的一部分或者由一个著名公司出品。而授权服务器只有在其他授权模式无法执行的情况下才能考虑使用这种模式。
适用场景公司搭建的授权服务器 A用户向客户端提供用户名和密码。
B客户端将用户名和密码发给认证服务器向后者请求令牌。
C认证服务器确认无误后向客户端提供访问令牌。 其它都一样修改OpenAuthorizationServerProvider 即可
using Microsoft.Owin.Security.OAuth;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Threading.Tasks;
using System.Security.Claims;
using System.Security.Principal;namespace WebApplication3.Provider
{/// summary/// 授权服务器配置/// /summarypublic class OpenAuthorizationServerProvider:OAuthAuthorizationServerProvider{public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context){//获取用户传入的用户名和密码string UserName context.UserName;string Password context.Password;//通过查数据库判断用户名和密码是否正确//以下只是一个示例用户名必须以test开头if (!UserName.StartsWith(test)){context.SetError(invalid_grant, 用户名或密码不正确);return;}//以下即为认证成功//通过查数据库得到一些用户的信息int userid 13;string role 管理员;string scope 权限1,权限2;var identity new ClaimsIdentity(context.Options.AuthenticationType);identity.AddClaim(new Claim(userid, userid.ToString()));identity.AddClaim(new Claim(role, role));identity.AddClaim(new Claim(scope, scope));context.Validated(identity);}/// summary/// 验证客户端信息/// /summary/// param namecontext/param/// returns/returnspublic override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context){可以验证 ClientID、ClientSecret或不验证//从上下文中获取ClientID和ClientSecretcontext.TryGetFormCredentials(out string clientId, out string clientSecret);//非法客户端if (clientId null || !clientId.StartsWith(AAA)){context.SetError(invalid_clientId, 客户端没有授权);return Task.FromResultobject(null);}//如果不验证可以直接执行下面的 验证通过context.Validated(); } }
}简化模式
有些 Web 应用是纯前端应用没有后端。必须将令牌储存在前端。RFC 6749 就规定了第二种方式允许直接向前端颁发令牌这种方式没有授权码这个中间步骤所以称为授权码“隐藏式”implicit
简化模式不通过第三方应用程序的服务器直接在浏览器中向授权服务器申请令牌跳过了授权码这个步骤所有步骤在浏览器中完成令牌对访问者是可见的且客户端不需要认证。所以 不会触发 ValidateClientAuthentication
这种方式把令牌直接传给前端是很不安全的。因此只能用于一些安全要求不高的场景并且令牌的有效期必须非常短通常就是会话期间session有效浏览器关掉令牌就失效了。 using Microsoft.Owin.Security.OAuth;
using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using WebApplication3.Provider;namespace WebApplication3.App_Start
{public class AuthorizationConfig{public static void Register(Owin.IAppBuilder app){OAuthAuthorizationServerOptions options new OAuthAuthorizationServerOptions(){AllowInsecureHttp true,//允许http而非https访问AuthenticationMode Microsoft.Owin.Security.AuthenticationMode.Active,//激活授权码模式TokenEndpointPath new Microsoft.Owin.PathString(value: /token),//访问host/token获取AccessTokenAuthorizeEndpointPath new Microsoft.Owin.PathString(/auth),//访问host/auth获取授权码AccessTokenExpireTimeSpan TimeSpan.FromMinutes(30),//AccessToken在30分钟后过期Provider new OpenAuthorizationServerProvider(),//AccessToken的提供类// 简化模式 省略下面代码 简化模式又称为隐式授权码模式它是授权码模式的一个简化版本//AuthorizationCodeProvider new OpenAuthorizationCodeProvider(),//授权码的提供类 RefreshTokenProvider new OpenRefreshTokenProvider(),//生成RefreshToken的配置};app.UseOAuthAuthorizationServer(options);app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());}}
}using Microsoft.Owin.Security.OAuth;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Threading.Tasks;
using System.Security.Claims;
using System.Security.Principal;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Infrastructure;namespace WebApplication3.Provider
{/// summary/// 授权服务器配置/// /summarypublic class OpenAuthorizationServerProvider : OAuthAuthorizationServerProvider{/// summary/// 验证重定向URI是否合法/// 授权码模式、简化模式 都会触发/// /summary/// param namecontext/param/// returns/returnspublic override async Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context){string url context.RedirectUri;context.Validated(context.RedirectUri);}/// summary/// 验证请求信息是否合法/// 授权码模式、简化模式 都会触发/// /summary/// param namecontext/param/// returns/returnspublic override async Task ValidateAuthorizeRequest(OAuthValidateAuthorizeRequestContext context){if (context.AuthorizeRequest.ClientId.StartsWith(zen)){context.Validated();}else{context.Rejected();}}/// summary/// 完成认证跳转到重定向URI/// 授权码模式、简化模式 都会触发/// /summary/// param namecontext/param/// returns/returnspublic override async Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context){var identity new ClaimsIdentity(Bearer);context.OwinContext.Authentication.SignIn(identity);context.RequestCompleted();} }
}》》访问 》》直接跳转 access_token 是通过锚链接的
授权码模式
一、是获取授权码 二、是获取AccessToken
在获取授权码时我们需要请求host/auth这个地址输入的参数有以下要求
1grant_type必须为authorization_code。
2response_type必须为code。
3client_id客户端ID。
4redirect_uri重定向地址如为http://abc.com/ 则请求授权码完成后将会重定向到http://abc.com/code[授权码]。
5scope授权范围可选。
6state客户端状态可选。 》》》Startup 同上 》》》JsonFormatConfig 同上 》》》api控制器同上 》》》OpenRefreshTokenProvider 刷新token 同上 》》》 AuthorizationConfig 类 using Microsoft.Owin.Security.OAuth;
using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using WebApplication3.Provider;namespace WebApplication3.App_Start
{public class AuthorizationConfig{public static void Register(Owin.IAppBuilder app){OAuthAuthorizationServerOptions options new OAuthAuthorizationServerOptions(){AllowInsecureHttp true,//允许http而非https访问AuthenticationMode Microsoft.Owin.Security.AuthenticationMode.Active,//激活授权码模式TokenEndpointPath new Microsoft.Owin.PathString(value: /token),//访问host/token获取AccessTokenAuthorizeEndpointPath new Microsoft.Owin.PathString(/auth),//访问host/auth获取授权码AccessTokenExpireTimeSpan TimeSpan.FromMinutes(30),//AccessToken在30分钟后过期Provider new OpenAuthorizationServerProvider(),//AccessToken的提供类// 简化模式 省略下面代码 简化模式又称为隐式授权码模式它是授权码模式的一个简化版本AuthorizationCodeProvider new OpenAuthorizationCodeProvider(),//授权码的提供类 RefreshTokenProvider new OpenRefreshTokenProvider(),//生成RefreshToken的配置};app.UseOAuthAuthorizationServer(options);app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());}}
}》》》OpenAuthorizationServerProvider
using Microsoft.Owin.Security.OAuth;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Threading.Tasks;
using System.Security.Claims;
using System.Security.Principal;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Infrastructure;namespace WebApplication3.Provider
{/// summary/// 授权服务器配置/// /summarypublic class OpenAuthorizationServerProvider : OAuthAuthorizationServerProvider{/// summary/// 验证重定向URI是否合法/// 授权码模式、简化模式 都会触发/// /summary/// param namecontext/param/// returns/returnspublic override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context){string url context.RedirectUri;context.Validated(context.RedirectUri);return base.ValidateClientRedirectUri(context);}/// summary/// 验证请求信息是否合法/// 授权码模式、简化模式 都会触发/// /summary/// param namecontext/param/// returns/returnspublic override async Task ValidateAuthorizeRequest(OAuthValidateAuthorizeRequestContext context){if (context.AuthorizeRequest.ClientId.StartsWith(zen)){context.Validated();}else{context.Rejected();}}/// summary/// 完成认证跳转到重定向URI/// 授权码模式、简化模式 都会触发/// /summary/// param namecontext/param/// returns/returnspublic override async Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context){//授权码模式var redirectUri context.Request.Query[redirect_uri];var clientId context.Request.Query[client_id];var identity new ClaimsIdentity(new GenericIdentity(clientId, OAuthDefaults.AuthenticationType)); var authorizeCodeContext new AuthenticationTokenCreateContext(context.OwinContext,context.Options.AuthorizationCodeFormat,new AuthenticationTicket(identity,new AuthenticationProperties(new Dictionarystring, string{{client_id, clientId},{redirect_uri, redirectUri}}){IssuedUtc DateTimeOffset.UtcNow,ExpiresUtc DateTimeOffset.UtcNow.Add(context.Options.AccessTokenExpireTimeSpan)}));await context.Options.AuthorizationCodeProvider.CreateAsync(authorizeCodeContext);context.Response.Write(Uri.EscapeDataString(authorizeCodeContext.Token));//为了测试方便直接打印出code//正常使用时是把code加在重定向网址后面//context.Response.Redirect(redirectUri ?code Uri.EscapeDataString(authorizeCodeContext.Token));context.RequestCompleted();}/// summary/// 验证客户端///// /summary/// param namecontext/param/// returns/returnspublic override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context){string cd context.ClientId;string clientId, clientSecret;context.TryGetFormCredentials(out clientId, out clientSecret);if (!clientId.StartsWith(zen)){context.SetError(invalid_client, 未授权的客户端);return Task.FromResultobject(null); ;}context.Validated();return Task.FromResultobject(null);}/// summary/// 生成客户端模式Access_Token/// 还需要将对应的客户端信息存储在web中/// /summary/// param namecontext/param/// returns/returnspublic override Task GrantClientCredentials(OAuthGrantClientCredentialsContext context){//以下即为认证成功return base.GrantClientCredentials(context);}public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context){// 以下即为认证成功//var identity new ClaimsIdentity(context.Options.AuthenticationType);ClaimsIdentity identity new GenericIdentity(name: context.ClientId, type: OAuthDefaults.AuthenticationType);//通过查数据库得到一些用户的信息int userid 13;string role 管理员;string scope 权限1,权限2;identity.AddClaim(new Claim(userid, userid.ToString()));identity.AddClaim(new Claim(role, role));identity.AddClaim(new Claim(scope, scope));context.Validated(identity);return base.GrantResourceOwnerCredentials(context);}public override async Task ValidateTokenRequest(OAuthValidateTokenRequestContext context){if (context.TokenRequest.IsAuthorizationCodeGrantType){context.Validated();}else{context.Rejected();}}}
}》》》 OpenAuthorizationCodeProvider
using Microsoft.Owin.Security.Infrastructure;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web;namespace WebApplication3.Provider
{public class OpenAuthorizationCodeProvider : IAuthenticationTokenProvider{private Dictionarystring, string codes new Dictionarystring, string();public void Create(AuthenticationTokenCreateContext context){string new_code Guid.NewGuid().ToString(n);context.SetToken(new_code);//context.SerializeTicket() 生成tokencodes.Add(new_code, context.SerializeTicket());}public Task CreateAsync(AuthenticationTokenCreateContext context){Create(context);return Task.FromResultobject(null);}public void Receive(AuthenticationTokenReceiveContext context){string code context.Token;if (codes.ContainsKey(code)){string value codes[code];codes.Remove(code);context.DeserializeTicket(value);}}public Task ReceiveAsync(AuthenticationTokenReceiveContext context){Receive(context);return Task.FromResultobject(null);}}
}access_token
access_token不能暴露在浏览器那么该存放在哪 重定向传回access_token会使安全保密性要求极高的访问令牌暴露在浏览器增加访问令牌失窃风险。
在我看来重定向携带的参数在URL上http协议下重定向传回access_token的形式是没有经过数据加密的他会增加令牌失窃的风险。那么关于access_token存放在哪的问题个人认为通过授权码以及客户端id和secret共同校验后获取的access_token可以把access_token存放在localStorage中localStorage虽然是永久存储但是access_token会有一个有效期有效期到了之后即便access_token一直都存在但是有效期过后就无法访问到受保护资源。
》》》webstorage sessionStorage和localStorage sessionStorage和localStorage区别 **注意: **不同浏览器无法共享localStorage或sessionStorage中的信息。 相同浏览器的不同页面间【相同域名和端口】可以共享相同的 localStorage 但是不同页面或标签页间无法共享sessionStorage的信息。