OpenIddict 登录及详细流程解析
GitHub上实例都是集成了Identity来实现,我这里去掉了相关东西,实现自定义的登录满足自己的结构要求
服务端配置添加数据库服务以及定时任务服务
builder.Services.AddDbContext<OpenIdDbContext>(options => { options.UseMySql(constr, ServerVersion.AutoDetect(constr), builder => { builder.UseRelationalNulls(); builder.MigrationsAssembly("OpenIdService"); }); options.UseOpenIddict(); }).AddQuartz(options => { options.UseMicrosoftDependencyInjectionJobFactory(); options.UseSimpleTypeLoader(); options.UseInMemoryStore(); }).AddQuartzHostedService(options => options.WaitForJobsToComplete = true);
OpenIddict服务配置 根据需要而定
builder.Services.AddOpenIddict() .AddCore(options => { //配置OpenIddict以使用EntityFrameworkCore存储和模型。 注意: 调用replacedefaultenentities()来替换默认的OpenIddict实体。 options.UseEntityFrameworkCore().UseDbContext<OpenIdDbContext>(); //喜欢使用MongoDB的开发人员可以删除前面的代码行并配置OpenIddict使用指定的MongoDB数据库: // options.UseMongoDb() .UseDatabase(new MongoClient().GetDatabase("openiddict")); options.UseQuartz(); }) .AddServer(options => { //配置交互服务地址 options.SetAuthorizationEndpointUris("/connect/authorize") .SetDeviceEndpointUris("/connect/device") .SetIntrospectionEndpointUris("/connect/introspect") .SetRevocationEndpointUris("/connect/revocat") .SetUserinfoEndpointUris("/connect/userinfo") .SetVerificationEndpointUris("/connect/verify") .SetLogoutEndpointUris("/connect/logout") .SetTokenEndpointUris("/connect/token") //这是允许的模式 .AllowAuthorizationCodeFlow() .AllowClientCredentialsFlow() .AllowDeviceCodeFlow() .AllowHybridFlow() .AllowImplicitFlow() .AllowPasswordFlow() .AllowRefreshTokenFlow() .RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles) //提供给API校验Jwt令牌使用是配置 .AddEncryptionKey(new SymmetricSecurityKey( Convert.FromBase64String("DRjd/nduI3Efze123nvbNUfc/="))) // 加密凭证 、注册签名 .AddDevelopmentEncryptionCertificate().AddDevelopmentSigningCertificate() //强制客户端应用程序使用 Proof Key Code Exchange (PKCE) .RequireProofKeyForCodeExchange() .Configure(options => { options.CodeChallengeMethods.Add(CodeChallengeMethods.Plain); }) //配置 启用通过后的后续处理 .UseAspNetCore().EnableStatusCodePagesIntegration() .EnableAuthorizationEndpointPassthrough() .EnableLogoutEndpointPassthrough() .EnableTokenEndpointPassthrough() .EnableUserinfoEndpointPassthrough() .EnableVerificationEndpointPassthrough() .DisableTransportSecurityRequirement(); // 禁用HTTPS 在开发测试环境 #region 禁用忽略选项配置 //禁用授权信息存储 // options.DisableAuthorizationStorage(); // options.AcceptAnonymousClients(); // options.DisableScopeValidation(); // options.IgnoreEndpointPermissions() // .IgnoreGrantTypePermissions() // .IgnoreResponseTypePermissions() // .IgnoreScopePermissions(); options.DisableAccessTokenEncryption(); #endregion }).AddValidation(options => { options.UseLocalServer(); //强制授权条目验证 出于性能原因,OpenIddict 3.0在接收API请求时默认不检查授权条目的状态:即使附加的授权被撤销 //,访问令牌也被认为是有效的 options.EnableAuthorizationEntryValidation(); options.UseAspNetCore(); });
准备工作基本完成,如果你需要做一个服务登录界面,这是需要提供相关页面和认证服务代码,然后去完成相关页面逻辑
builder.Services.AddAuthentication(options => { options.DefaultScheme = UosoAuthenticationScheme.AuthenticationScheme; }).AddCookie(UosoAuthenticationScheme.AuthenticationScheme, options => { options.AccessDeniedPath = "/Account/Login"; options.LoginPath = "/Account/Login"; options.LogoutPath = "/Account/LogOut"; });
这里需要自定义AuthorizationController 来实现认证逻辑,这里可以参考官方例子代码说明
登录页面逻辑采用自己的方式如下实现 SignInAsync 方法完成
var properties = new AuthenticationProperties { IsPersistent = model.RememberMe, ExpiresUtc= DateTimeOffset.UtcNow.AddMinutes(30) }; var principal = CreateUserPrincpal(resultdata); await HttpContext.SignInAsync(UosoAuthenticationScheme.AuthenticationScheme, principal, properties);
这里放下AuthorizationController源码
public class AuthorizationController : Controller { private const string applicationname = UosoAuthenticationScheme.AuthenticationScheme; #region 注入OpenIddict相关交互接口 private readonly IOpenIddictApplicationManager _applicationManager; private readonly IOpenIddictAuthorizationManager _authorizationManager; private readonly IOpenIddictScopeManager _scopeManager; IAuthenticationSchemeProvider _schemeProvider; IUSUserLoginService _userService; #endregion #region 注入用户信息交互接口 //private readonly SignInManager<ApplicationUser> _signInManager; //private readonly UserManager<ApplicationUser> _userManager; #endregion public AuthorizationController( IOpenIddictApplicationManager applicationManager, IOpenIddictAuthorizationManager authorizationManager, IOpenIddictScopeManager scopeManager, IUSUserLoginService userService) { _applicationManager = applicationManager; _authorizationManager = authorizationManager; _scopeManager = scopeManager; _userService = userService; } #region 授权端点的操作 指定路由 这一步自己处理 private IEnumerable<string> GetDestinations(Claim claim) { // Note: by default, claims are NOT automatically included in the access and identity tokens. // To allow OpenIddict to serialize them, you must attach them a destination, that specifies // whether they should be included in access tokens, in identity tokens or in both. return claim.Type switch { Claims.Name or Claims.Subject => ImmutableArray.Create(Destinations.AccessToken, Destinations.IdentityToken), _ => ImmutableArray.Create(Destinations.AccessToken), }; } private IEnumerable<string> GetDestinations(Claim claim, ClaimsPrincipal principal) { switch (claim.Type) { case Claims.Name: yield return Destinations.AccessToken; if (principal.HasScope(Scopes.Profile)) yield return Destinations.IdentityToken; yield break; case Claims.Email: yield return Destinations.AccessToken; if (principal.HasScope(Scopes.Email)) yield return Destinations.IdentityToken; yield break; case Claims.Role: yield return Destinations.AccessToken; if (principal.HasScope(Scopes.Roles)) yield return Destinations.IdentityToken; yield break; case "AspNet.Identity.SecurityStamp": yield break; default: yield return Destinations.AccessToken; yield break; } } private ClaimsPrincipal CreateUserPrincpal(USUserLoginInfo resultdata, string claimsIdentityName = "USLOGININFO") { //登录成功流程 ClaimsIdentity identity = new ClaimsIdentity(claimsIdentityName); identity.AddClaim(new Claim(Claims.Subject, resultdata.Id + "")); identity.AddClaim(new Claim(Claims.Name, resultdata.UserName)); identity.AddClaim(new Claim(Claims.Nickname, resultdata.NickName)); identity.AddClaim(new Claim("tenantid", resultdata.TenantId + "")); identity.AddClaim(new Claim("organizes", resultdata.Organizes + "")); identity.AddClaim(new Claim("usergroups", resultdata.UserGroups)); identity.AddClaim(new Claim("usertype", resultdata.UserType + "")); identity.AddClaim(new Claim("userroles", resultdata.Roles + "")); identity.AddClaim(new Claim("userposts", resultdata.Posts + "")); return new ClaimsPrincipal(identity); } /// <summary> /// 登录权限校验 /// </summary> /// <returns></returns> /// <exception cref="InvalidOperationException"></exception> /// <exception cref="Exception"></exception> [HttpGet("~/connect/authorize")] [HttpPost("~/connect/authorize")] [IgnoreAntiforgeryToken] public async Task<IActionResult> Authorize() { // var s=await _schemeProvider.GetAllSchemesAsync(); //通过扩展的获取自定义的参数校验 var request = HttpContext.GetOpenIddictServerRequest() ?? throw new InvalidOperationException("未获取到相关认证情况"); #region 存在登录凭证且明确了登录请求的行为 // 存在登录凭证且明确了登录请求的行为 if (request.HasPrompt(Prompts.Login)) { //这里有个小坑,在Challenge之前必须把这个行为去掉 不然 Challenge 进入 /connect/authorize 路由陷入死循环 var prompt = string.Join(" ", request.GetPrompts().Remove(Prompts.Login)); var parameters = Request.HasFormContentType ? Request.Form.Where(parameter => parameter.Key != Parameters.Prompt).ToList() : Request.Query.Where(parameter => parameter.Key != Parameters.Prompt).ToList(); parameters.Add(KeyValuePair.Create(Parameters.Prompt, new StringValues(prompt))); return Challenge( authenticationSchemes: applicationname, // IdentityConstants.ApplicationScheme, properties: new AuthenticationProperties { RedirectUri = Request.PathBase + Request.Path + QueryString.Create(parameters) }); } #endregion //检索本地的Cookies信息 确定重定向页面 这里都是UTC时间来设置的过期情况 这里没有用Identity 所以这里可以指定自己的应用名称 var result = await HttpContext.AuthenticateAsync(applicationname); //IdentityConstants.ApplicationScheme #region 未获取本地Cookies信息或者 cookie过期的情况 if (request == null || !result.Succeeded || (request.MaxAge != null && result.Properties?.IssuedUtc != null && DateTimeOffset.UtcNow - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value))) { //是否是无效授权 if (request.HasPrompt(Prompts.None)) { return Forbid( authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, properties: new AuthenticationProperties(new Dictionary<string, string?> { [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.LoginRequired, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "用户未登录." })); } return Challenge( authenticationSchemes: applicationname, properties: new AuthenticationProperties { RedirectUri = Request.PathBase + Request.Path + QueryString.Create( Request.HasFormContentType ? Request.Form.ToList() : Request.Query.ToList()) }); } #endregion var resultdata = _userService.GetLoginInfo(new Guid(result.Principal.GetClaim(Claims.Subject) ?? throw new Exception("用户标识存在"))) ?? throw new Exception("用户详细信息不存在"); // 获取客户端详细信息 验证其他数据 var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ?? throw new InvalidOperationException("未查找到该客户端的应用详细信息"); //查找当前情况客户端下请求用户的持久化授权数据信息 var authorizations = await _authorizationManager.FindAsync( subject: resultdata.Id.ToString(), client: await _applicationManager.GetIdAsync(application) ?? throw new Exception("没有找到客户端的应用信息"), //这里区分下 是application的Id而不是ClientId status: Statuses.Valid, type: AuthorizationTypes.Permanent, scopes: request.GetScopes()).ToListAsync(); var consenttype = await _applicationManager.GetConsentTypeAsync(application); //获取授权同意确认页面 switch (consenttype) { //判断授权同意的类型 //1 外部允许的且没有任何授权项 case ConsentTypes.External when !authorizations.Any(): return Forbid( authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, properties: new AuthenticationProperties(new Dictionary<string, string?> { [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "登录用户没用访问该客户端应用的权限" })); // 隐式、外部授权、显示模式模式 case ConsentTypes.Implicit: case ConsentTypes.External when authorizations.Any(): case ConsentTypes.Explicit when authorizations.Any() && !request.HasPrompt(Prompts.Consent): ClaimsPrincipal principal = CreateUserPrincpal(resultdata); //设置请求的范围 principal.SetScopes(request.GetScopes()); //查找scope允许访问的资源 var resources = await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync(); //通过扩展设置不同的资源访问 其实本质都是设置Claims 只是 key 在 scope以及Resource上不同 //Resource = "oi_rsrc"; // Scope = "oi_scp"; principal.SetResources(resources); // 自动创建一个永久授权,以避免需要明确的同意 用于包含相同范围的未来授权或令牌请求 var authorization = authorizations.LastOrDefault(); if (authorization is null) { authorization = await _authorizationManager.CreateAsync( principal: principal, subject: resultdata.Id.ToString(), client: await _applicationManager.GetIdAsync(application), type: AuthorizationTypes.Permanent, scopes: principal.GetScopes()); } principal.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization)); foreach (var claim in principal.Claims) { claim.SetDestinations(GetDestinations(claim, principal)); } //登录 OpenIddict签发令牌 return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); // At this point, no authorization was found in the database and an error must be returned // if the client application specified prompt=none in the authorization request. case ConsentTypes.Explicit when request.HasPrompt(Prompts.None): case ConsentTypes.Systematic when request.HasPrompt(Prompts.None): return Forbid( authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, properties: new AuthenticationProperties(new Dictionary<string, string?> { [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "Interactive user consent is required." })); // In every other case, render the consent form. default: return View(new AuthorizeViewModel { ApplicationName = await _applicationManager.GetLocalizedDisplayNameAsync(application), Scope = request.Scope }); } } #region 同意、拒绝逻辑 [Authorize, FormValueRequired("submit.Accept")] [HttpPost("~/connect/authorize"), ValidateAntiForgeryToken] public async Task<IActionResult> Accept() { var request = HttpContext.GetOpenIddictServerRequest() ?? throw new InvalidOperationException("The OpenID Connect request cannot be retrieved."); var resultdata = _userService.GetLoginInfo(new Guid(User.GetClaim(Claims.Subject) ?? throw new Exception("用户标识存在"))) ?? throw new Exception("用户详细信息不存在"); // 获取客户端详细信息 验证其他数据 var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ?? throw new InvalidOperationException("未查找到该客户端的应用详细信息"); //查找当前情况客户端下请求用户的持久化授权数据信息 var authorizations = await _authorizationManager.FindAsync( subject: resultdata.Id.ToString(), client: await _applicationManager.GetIdAsync(application) ?? throw new Exception("没有找到客户端的应用信息"), //这里区分下 是application的Id而不是ClientId status: Statuses.Valid, type: AuthorizationTypes.Permanent, scopes: request.GetScopes()).ToListAsync(); if (!authorizations.Any() && await _applicationManager.HasConsentTypeAsync(application, ConsentTypes.External)) { return Forbid( authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, properties: new AuthenticationProperties(new Dictionary<string, string?> { [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The logged in user is not allowed to access this client application." })); } ClaimsPrincipal principal = CreateUserPrincpal(resultdata); principal.SetScopes(request.GetScopes()); principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync()); var authorization = authorizations.LastOrDefault(); if (authorization is null) { authorization = await _authorizationManager.CreateAsync( principal: principal, subject: resultdata.Id.ToString(), client: await _applicationManager.GetIdAsync(application) ?? throw new Exception("未找到客户端应用信息"), type: AuthorizationTypes.Permanent, scopes: principal.GetScopes()); } principal.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization)); foreach (var claim in principal.Claims) { claim.SetDestinations(GetDestinations(claim, principal)); } return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } [Authorize, FormValueRequired("submit.Deny")] [HttpPost("~/connect/authorize"), ValidateAntiForgeryToken] public IActionResult Deny() => Forbid(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); #endregion #endregion #region 获取Token地址 包括所有方式 /// <summary> /// 可以指定不同的获取Token的客户端逻辑 /// </summary> /// <returns></returns> /// <exception cref="InvalidOperationException"></exception> [HttpPost("~/connect/token"), Produces("application/json")] public async Task<IActionResult> Exchange() { var request = HttpContext.GetOpenIddictServerRequest() ?? throw new InvalidOperationException("OIDC请求不存在."); if (request.IsClientCredentialsGrantType()) { var application = await _applicationManager.FindByClientIdAsync(request.ClientId); if (application == null) { throw new InvalidOperationException("当前客户端应用不存在"); } var identity = new ClaimsIdentity( TokenValidationParameters.DefaultAuthenticationType, Claims.Name, Claims.Role); // Use the client_id as the subject identifier. identity.AddClaim(Claims.Subject, await _applicationManager.GetClientIdAsync(application), Destinations.AccessToken, Destinations.IdentityToken); identity.AddClaim(Claims.Name, await _applicationManager.GetDisplayNameAsync(application), Destinations.AccessToken, Destinations.IdentityToken); var principal = new ClaimsPrincipal(identity); principal.SetScopes(request.GetScopes()); principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync()); foreach (var claim in principal.Claims) { claim.SetDestinations(GetDestinations(claim)); } return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } else if (request.IsPasswordGrantType()) { var result = _userService.UserLogin(new LoginModel { username = request?.Username, userpwd = request.Password }); if (!result.IsSuccess) { throw new OpenIddictExceptions.ValidationException(result.Message); //return Forbid( // authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, // properties: new AuthenticationProperties(new Dictionary<string, string?> // { // [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant, // [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = result.Message // })); } var resultdata = result.Data as USUserLoginInfo ?? throw new Exception("登录用户信息异常"); var principal = CreateUserPrincpal(resultdata); //这是密码模式能方位那些 principal.SetScopes(new[] { Scopes.OpenId, Scopes.Email, Scopes.Profile, Scopes.Roles, "user_api", "openiddict_api" }); principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync()); foreach (var claim in principal.Claims) { claim.SetDestinations(GetDestinations(claim, principal)); } return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } if (request.IsAuthorizationCodeGrantType() || request.IsDeviceCodeGrantType() || request.IsRefreshTokenGrantType()) { var principal = (await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)).Principal; // principal.Identity.IsAuthenticated var user = _userService.GetLoginInfo(new Guid(principal?.GetClaim(Claims.Subject) ?? throw new Exception("用户标识存在"))); if (user == null) { return Forbid( authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, properties: new AuthenticationProperties(new Dictionary<string, string?> { [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant, [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "令牌已失效" })); } // Ensure the user is still allowed to sign in. //if (!await _signInManager.CanSignInAsync(user)) //{ // return Forbid( // authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, // properties: new AuthenticationProperties(new Dictionary<string, string?> // { // [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant, // [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is no longer allowed to sign in." // })); //} foreach (var claim in principal.Claims) { claim.SetDestinations(GetDestinations(claim, principal)); } return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } throw new InvalidOperationException("The specified grant type is not supported."); } [HttpGet("~/connect/logout")] public IActionResult Logout() => View(); [ActionName(nameof(Logout)), HttpPost("~/connect/logout"), ValidateAntiForgeryToken] public async Task<IActionResult> LogoutPost() { await HttpContext.SignOutAsync(); return SignOut( authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, properties: new AuthenticationProperties { RedirectUri = "/" }); } #endregion }
整个逻辑如下我画一张图结合关键代码来诠释它
先说WebSite通过 OpenIdConnect 来完成登录认证 ,最后都是交给中间件中的signin-oidc页面来完成客户端Cookie登录状态维持,如果在客户端我们注释掉会发生什么
services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; // options.RequireAuthenticatedSignIn = true; }) // .AddCookie(options => //{ // options.LoginPath = "/"; // options.ExpireTimeSpan = TimeSpan.FromMinutes(50); // options.SlidingExpiration = false; //}) .AddOpenIdConnect(options => { // Note: these settings must match the application details // inserted in the database at the server level. options.ClientId = "mvc_test"; options.ClientSecret = "911564A5-E3FE-42CB-B10D-61EF6A8B3654"; options.RequireHttpsMetadata = false; options.GetClaimsFromUserInfoEndpoint = true; options.SaveTokens = false; options.UsePkce=true; options.Prompt = OpenIdConnectPrompt.Login; options.ResponseType = OpenIdConnectResponseType.Code; options.AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet; //options.Prompt = "login"; options.Authority = "http://localhost:5276"; //options.Scope.Add("openid"); options.Scope.Add("profile"); options.Scope.Add("email"); options.Scope.Add("roles"); options.Scope.Add("user_api"); options.Scope.Add("openiddict_api"); options.SecurityTokenValidator = new JwtSecurityTokenHandler { InboundClaimTypeMap = new Dictionary<string, string>() }; options.TokenValidationParameters.NameClaimType = "name"; options.TokenValidationParameters.RoleClaimType = "role"; options.AccessDeniedPath = "/"; });
这就是为什么我们写回调地址 要写 http://localhost:44381/signin-oidc 的signin-oidc ,如果要写其他的,需要我们自己处理,这里OpenIdConnect中间件帮我们处理了,这其中包括维持客户端的登录状态,类似我们用其他第三方登录一样回调后需要按某个协议来处理,如果客户端设置了
options.GetClaimsFromUserInfoEndpoint = true; options.SaveTokens = false;
GetClaimsFromUserInfoEndpoint=true 需要服务端准备UserInfo接口否则会报错
SaveTokens=true 客户端可以拿到 var token = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);拿到AccessToken
WebAPI 可结合服务端配置的
AddEncryptionKey(new SymmetricSecurityKey( Convert.FromBase64String("DRjd/nduIen99BvbNUfc/VKg7Kbk9sMkY=")))
Token令牌的签发 /connect/token 接口 根据请求模式签发对应令牌
if (request.IsClientCredentialsGrantType()) { //处理 } else if (request.IsPasswordGrantType()) { //处理 } else if (request.IsAuthorizationCodeGrantType() || request.IsDeviceCodeGrantType() || request.IsRefreshTokenGrantType()) { //处理 }
SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
通过这个签发令牌,那么令牌默认Access_Token周期是3600s,当然我们可以在生成规则这里改,这里不像IdentityServer4一样处理到了Client配置里面,但是令牌周期一般也没啥变化改动少,但是通过如下设置
principal.SetAccessTokenLifetime(TimeSpan.FromSeconds(7200)); principal.SetAuthorizationCodeLifetime(TimeSpan.FromSeconds(300)); principal.SetDeviceCodeLifetime(TimeSpan.FromSeconds(7200)); principal.SetRefreshTokenLifetime(TimeSpan.FromSeconds(7200)); principal.SetUserCodeLifetime(TimeSpan.FromSeconds(7200)); return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
源码
public static TimeSpan? GetAccessTokenLifetime(this ClaimsPrincipal principal) => GetLifetime(principal, Claims.Private.AccessTokenLifetime); private static TimeSpan? GetLifetime(ClaimsPrincipal principal, string type) { if (principal is null) { throw new ArgumentNullException(nameof(principal)); } var value = principal.GetClaim(type); if (string.IsNullOrEmpty(value)) { return null; } if (double.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out double result)) { return TimeSpan.FromSeconds(result); } return null; }
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!
本文版权归作者和博客园共有,来源网址:http://www.cnblogs.com/liyouming欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接。