OpenIddict 登入及詳細流程解析

龍碼精神發表於2022-01-07

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原始碼

OpenIddict 登入及詳細流程解析
 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())
            {
                  //處理
             }

 

 

相關文章