zoukankan      html  css  js  c++  java
  • 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/GnduI3Efzen9V9BvbNUfc/VKgXltV7Kbk9sMkY=")))
              // 加密凭证 、注册签名
              .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
        }
    AuthorizationController

    整个逻辑如下我画一张图结合关键代码来诠释它

    先说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";
                options.ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654";
    
                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/GnduI3Efzen9V9BvbNUfc/VKgXltV7Kbk9sMkY=")))

     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欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接
  • 相关阅读:
    链接错误error LNK2005可能原因之一
    ACCESS一些特殊数据类型
    flex&bison学习笔记(2)
    经典小故事
    spoj 2939 Query on a tree V 动态树分治
    spoj 913 Query on a tree II 倍增
    CEOI 2004 sweets 容斥原理
    poj 1741 Tree 树的分治
    zjoi 2007 hide 捉迷藏 动态树分治
    spoj 2798 Query on a tree again! 树链剖分
  • 原文地址:https://www.cnblogs.com/liyouming/p/15772403.html
Copyright © 2011-2022 走看看