net Framework OAuth2.0
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>/// 授权服务器配置/// </summary>public class OpenAuthorizationServerProvider:OAuthAuthorizationServerProvider{/// <summary>/// 验证客户端信息/// </summary>/// <param name="context"></param>/// <returns></returns>public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context){//如果是刷新token,且不要验证。if (context.Parameters.Get("refresh_token") == null){//获取clientId,ClientSecretstring 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 name="context"></param>/// <returns></returns>public 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配置/// </summary>public class OpenRefreshTokenProvider:AuthenticationTokenProvider{private static ConcurrentDictionary<string, string> _refreshTokens = new ConcurrentDictionary<string, string>();/// <summary>/// 生成Refresh_token/// </summary>/// <param name="context"></param>public 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 name="context"></param>public 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;Dictionary<string,string> lst = new Dictionary<string,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>/// 授权服务器配置/// </summary>public 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 name="context"></param>/// <returns></returns>public 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.FromResult<object>(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>/// 授权服务器配置/// </summary>public class OpenAuthorizationServerProvider : OAuthAuthorizationServerProvider{/// <summary>/// 验证重定向URI是否合法/// 授权码模式、简化模式 都会触发/// </summary>/// <param name="context"></param>/// <returns></returns>public override async Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context){string url = context.RedirectUri;context.Validated(context.RedirectUri);}/// <summary>/// 验证请求信息是否合法/// 授权码模式、简化模式 都会触发/// </summary>/// <param name="context"></param>/// <returns></returns>public override async Task ValidateAuthorizeRequest(OAuthValidateAuthorizeRequestContext context){if (context.AuthorizeRequest.ClientId.StartsWith("zen")){context.Validated();}else{context.Rejected();}}/// <summary>/// 完成认证,跳转到重定向URI/// 授权码模式、简化模式 都会触发/// </summary>/// <param name="context"></param>/// <returns></returns>public override async Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context){var identity = new ClaimsIdentity("Bearer");context.OwinContext.Authentication.SignIn(identity);context.RequestCompleted();} }
}
》》访问
》》直接跳转 access_token 是通过锚链接的
授权码模式 :
一、是获取授权码,
二、是获取AccessToken
在获取授权码时,我们需要请求host/auth这个地址,输入的参数有以下要求:
(1)grant_type,必须为authorization_code。
(2)response_type,必须为code。
(3)client_id,客户端ID。
(4)redirect_uri,重定向地址,如为http://abc.com/,
则请求授权码完成后,将会重定向到:http://abc.com/code=[授权码]。
(5)scope,授权范围,可选。
(6)state,客户端状态,可选。
》》》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>/// 授权服务器配置/// </summary>public class OpenAuthorizationServerProvider : OAuthAuthorizationServerProvider{/// <summary>/// 验证重定向URI是否合法/// 授权码模式、简化模式 都会触发/// </summary>/// <param name="context"></param>/// <returns></returns>public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context){string url = context.RedirectUri;context.Validated(context.RedirectUri);return base.ValidateClientRedirectUri(context);}/// <summary>/// 验证请求信息是否合法/// 授权码模式、简化模式 都会触发/// </summary>/// <param name="context"></param>/// <returns></returns>public override async Task ValidateAuthorizeRequest(OAuthValidateAuthorizeRequestContext context){if (context.AuthorizeRequest.ClientId.StartsWith("zen")){context.Validated();}else{context.Rejected();}}/// <summary>/// 完成认证,跳转到重定向URI/// 授权码模式、简化模式 都会触发/// </summary>/// <param name="context"></param>/// <returns></returns>public 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 Dictionary<string, 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 name="context"></param>/// <returns></returns>public 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.FromResult<object>(null); ;}context.Validated();return Task.FromResult<object>(null);}/// <summary>/// 生成客户端模式Access_Token/// 还需要将对应的客户端信息存储在web中/// </summary>/// <param name="context"></param>/// <returns></returns>public 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 Dictionary<string, string> codes = new Dictionary<string, 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.FromResult<object>(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.FromResult<object>(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的信息。
相关文章:

net Framework OAuth2.0
grant_type client_credentials 客户端凭证password 密码模式 用于资源所有者密码凭据token 隐藏式 、 简化式 简化模式又称为隐式授权码模式,它是授权码模式的一个简化版本authorization_code 授权码 A. 第三方程序向资源拥有者(用户)发送授权请求…...
速盾:服务器cdn加速超时如何解决?
CDN(Content Delivery Network,内容分发网络)是一种将网站内容分布到全球各地服务器上的技术,以提高网站的访问速度和用户体验。然而,在使用CDN时,有时候会遇到服务器CDN加速超时的问题,即CDN服…...

2024年6月总结及随笔之打卡网红点
1. 回头看 日更坚持了547天。 读《人工智能时代与人类未来》更新完成读《AI未来进行式》开更并更新完成读《AI新生:破解人机共存密码》开更并持续更新 2023年至2024年6月底累计码字1267912字,累计日均码字2317字。 2024年6月码字90659字,…...

《Windows API每日一练》7.4 状态报告上使用计时器
这一节我们使用计时器,每隔一秒获取当前鼠标坐标位置的像素值,并显示在窗口,这就相当于是一个简单的取色器了。 本节必须掌握的知识点: 第47练:取色器 7.4.1 第47练:取色器 /*----------------------------…...
python实现API调用缓存
python实现API调用缓存 1.代码2.输出3.保存的json数据 想把python某些函数的参数及返回值记录下来,如果之前已计算过,则直接返回缓存中的数据 1.代码 import jsondef get_variable_name(var):变量转变量名local_varsglobals()return [name for name, value in local_vars.ite…...

传输距离3000M|低延迟|48K采样音频传输模块-SA356大功率发射模块
无线音频应用中,远距离音频传输在许多领域具有广泛的应用需求,例如大型会议系统、公共广播、户外活动和音乐演出等。为了满足这些需求,音频传输模块需要具备一些关键特性,包括长距离传输能力、高音质、低延迟、稳定性以及抗干扰能…...
前端css性能优化
前端css性能优化 1. 减少样式表数量和压缩文件大小: 通过合并多个样式表、删除未使用的样式、压缩样式表等方式来减少样式表数量和大小,从而减少网络请求和提高加载速度。 通常来说,样式文件会被浏览器缓存,进入到其他页面样式文件…...
如何在Windows上使用Docker搭建PHP开发环境
前言 在本地搭建开发环境我好像没几年就要折腾一次,因为本地开发电脑使用的是windows,早些年的时候,用过很多类似WAMP之类的东西,但最终都有或多或少不满意的地方,前两年的时候,还折腾过WSL,但…...
java 单例模式
Java中实现单例模式的常见方式有两种:懒汉式和饿汉式。以下是这两种方式的简单示例: 饿汉式 饿汉式单例模式在类加载时就完成了实例的初始化,以空间换时间,确保了实例的唯一性。 public class Singleton {// 在自己内部定义自己…...
爬虫 属性 方法
在Python中,爬虫常用于从网页上抓取数据。BeautifulSoup是一个流行的库,用于从HTML或XML文件中提取数据。它创建了一个解析树,方便你从文档中查找、修改或导航数据。 安装BeautifulSoup 首先,你需要安装BeautifulSoup和lxml&…...

HEX文件
什么是hex文件 以*.hex为后缀的文件我们称之为HEX文件。hex是intel规定的标准,hex的全称是Intel HEX,此类文件通常用于传输将被存于ROM或EEPROM中的程序和数据。是由一行行符合Intel HEX文件格式的文本所构成的ASCII文本文件。HEX的英语原始意思是16进制…...
人机融合的智能操作系统
操作系统(Operating System,简称 OS)是管理计算机硬件与软件资源的系统软件,同时也是计算机系统的内核与基石。它的职责常包括对硬件的直接监管、对各种计算资源(如内存、处理器时间等)的管理、以及提供诸如…...

数据结构之二叉树概念
数据结构之二叉树 二叉树简介分类普通二叉树平衡二叉树满二叉树二叉搜索树(二叉排序树、二叉查找树),平衡二叉树红黑树 B树类型B树(B-树、B_树)B树B*树 二叉树 简介 二叉树(Binary Tree) :是一种非常重要…...

Linux源码阅读笔记08-进程调度API系统调用案例分析
kthread_create_on_node kthread_create_on_node函数功能:指定存储节点创建新内核线程。源码如下: 操作实战 #include <linux/module.h> #include <linux/pid.h> #include <linux/sched.h> #include <linux/kthread.h> #inclu…...

短视频抓取:成都柏煜文化传媒有限公司
短视频抓取:技术挑战、法律边界与未来趋势 随着移动互联网的迅猛发展,短视频平台如雨后春笋般涌现,成为现代人生活娱乐的重要组成部分。然而,在海量短视频内容中,如何高效、准确地抓取目标视频,成为了一个…...
proto的前后端使用
首先定义一个input.proto文件 内容如下 syntax "proto3";message InputData {int32 UserId 1; // 将 number 改为 int32 或 int64string UserInput 2;string DrunkState 3; }message ResponseData {string AIResponse 1;string prompt 2;string emoti…...

华为解决固态硬盘致命弱点:延长30~50%的SSD寿命
如今的SSD容量越做越大,企业级SSD容量已达30TB、60TB的、甚至120TB。但SSD寿命一直是“致命伤”,越大容量的盘,出现故障后,丢失的数据就越多。 近日, 华为数据存储发文,揭开华为全闪分布式存储让SSD大盘更“…...
登录验证码高扩展性设计方案
登录验证码高扩展性建设方案 本文分享了一种登录验证码高扩展性的建设方案,通过工厂模式策略模式,增强了验证码服务中验证码生成器、验证码存储器、验证码图片生成器的扩展性,实现了服务组件的多样化,降低了维护成本 登录验证码高…...
Spring MVC数据绑定和响应——数据回写(一)普通字符串的回写
接下来通过HttpServletResponse输出数据的案例,演示普通字符串的回写,案例具体实现步骤如下。 1、创建一个数据回写类DataController,在DataController类中定义showDataByResponse()方法,用于测试在Spring MVC中普通字符串的回写…...
怎样才能更好地保护个人账号的安全
怎样才能更好地保护个人账号的安全 保护个人账号安全是网络安全的重要组成部分,以下是一些有效的措施来增强账号的安全性: 1. 使用强密码 复杂性:创建包含大小写字母、数字和特殊字符的密码。长度:密码至少应有12个字符长。唯一…...

Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...

GitFlow 工作模式(详解)
今天再学项目的过程中遇到使用gitflow模式管理代码,因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存,无论是github还是gittee,都是一种基于git去保存代码的形式,这样保存代码…...

解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...
0x-3-Oracle 23 ai-sqlcl 25.1 集成安装-配置和优化
是不是受够了安装了oracle database之后sqlplus的简陋,无法删除无法上下翻页的苦恼。 可以安装readline和rlwrap插件的话,配置.bahs_profile后也能解决上下翻页这些,但是很多生产环境无法安装rpm包。 oracle提供了sqlcl免费许可,…...
6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础
第三周 Day 3 🎯 今日目标 理解类(class)和对象(object)的关系学会定义类的属性、方法和构造函数(init)掌握对象的创建与使用初识封装、继承和多态的基本概念(预告) &a…...

GraphQL 实战篇:Apollo Client 配置与缓存
GraphQL 实战篇:Apollo Client 配置与缓存 上一篇:GraphQL 入门篇:基础查询语法 依旧和上一篇的笔记一样,主实操,没啥过多的细节讲解,代码具体在: https://github.com/GoldenaArcher/graphql…...

Android写一个捕获全局异常的工具类
项目开发和实际运行过程中难免会遇到异常发生,系统提供了一个可以捕获全局异常的工具Uncaughtexceptionhandler,它是Thread的子类(就是package java.lang;里线程的Thread)。本文将利用它将设备信息、报错信息以及错误的发生时间都…...