IdentityServer4 自定義授權模式


IdentityServer4除了提供常規的幾種授權模式外(AuthorizationCode、ClientCredentials、Password、RefreshToken、DeviceCode),還提供了可以拓展的授權模式,下面就根據源碼簡單說下IdentityServer4是如何實現自定義授權模式的。

一、查看IdentityServer4自定義授權模式源碼

當用戶請求 connect/token 地址時,會執行TokenRequestValidator類的ValidateRequestAsync方法,在ValidateRequestAsync方法中根據GrantTypes類型調用不同的Validate方法,如下:

public async Task<TokenRequestValidationResult> ValidateRequestAsync(NameValueCollection parameters, ClientSecretValidationResult clientValidationResult)
        {
            //去掉了部分代碼。。。

            _validatedRequest.GrantType = grantType;

            switch (grantType)
            {
                case OidcConstants.GrantTypes.AuthorizationCode:
                    return await RunValidationAsync(ValidateAuthorizationCodeRequestAsync, parameters);
                case OidcConstants.GrantTypes.ClientCredentials:
                    return await RunValidationAsync(ValidateClientCredentialsRequestAsync, parameters);
                case OidcConstants.GrantTypes.Password:
                    return await RunValidationAsync(ValidateResourceOwnerCredentialRequestAsync, parameters);
                case OidcConstants.GrantTypes.RefreshToken:
                    return await RunValidationAsync(ValidateRefreshTokenRequestAsync, parameters);
                case OidcConstants.GrantTypes.DeviceCode:
                    return await RunValidationAsync(ValidateDeviceCodeRequestAsync, parameters);
                default:
                    return await RunValidationAsync(ValidateExtensionGrantRequestAsync, parameters);
            }
        }

自定義的授權模式會進入 ValidateExtensionGrantRequestAsync 方法 ,方法中會做一些簡單的驗證,然后調用 _extensionGrantValidator.ValidateAsync 方法,_extensionGrantValidator.ValidateAsync方法實現如下:

public class ExtensionGrantValidator
    {
        private readonly ILogger _logger;
        private readonly IEnumerable<IExtensionGrantValidator> _validators;//可以注入多個自定義授權類,用集合保存

        /// <summary>
        /// Initializes a new instance of the <see cref="ExtensionGrantValidator"/> class.
        /// </summary>
        /// <param name="validators">The validators.</param>
        /// <param name="logger">The logger.</param>
        public ExtensionGrantValidator(IEnumerable<IExtensionGrantValidator> validators, ILogger<ExtensionGrantValidator> logger)
        {
            if (validators == null)
            { 
                _validators = Enumerable.Empty<IExtensionGrantValidator>();
            }
            else
            {
                //把注入的自定義授權類放入集合中
                _validators = validators;
            }

            _logger = logger;
        }

        /// <summary>
        /// 得到所有可用的自定義授權類型
        /// </summary>
        /// <returns></returns>
        public IEnumerable<string> GetAvailableGrantTypes()
        {
            return _validators.Select(v => v.GrantType);
        }

        /// <summary>
        /// Validates the request.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns></returns>
        public async Task<GrantValidationResult> ValidateAsync(ValidatedTokenRequest request)
        {
            //根據用戶請求的GrantType獲取自定義授權類
            var validator = _validators.FirstOrDefault(v => v.GrantType.Equals(request.GrantType, StringComparison.Ordinal));

            if (validator == null)
            {
                _logger.LogError("No validator found for grant type");
                return new GrantValidationResult(TokenRequestErrors.UnsupportedGrantType);
            }

            try
            {
                _logger.LogTrace("Calling into custom grant validator: {type}", validator.GetType().FullName);

                var context = new ExtensionGrantValidationContext
                {
                    Request = request
                };
                //執行驗證方法,這里執行的就是我們自定義授權的驗證方法
                await validator.ValidateAsync(context);
                return context.Result;
            }
            catch (Exception e)
            {
                _logger.LogError(1, e, "Grant validation error: {message}", e.Message);
                return new GrantValidationResult(TokenRequestErrors.InvalidGrant);
            }
        }
    }

二、實現自定義授權

 比如我們想實現短信登錄,就可以自定義一個授權類型 SMSGrantType 

1、創建自定義授權類 SMSGrantValidator  ,實現IExtensionGrantValidator接口

public class SMSGrantValidator : IExtensionGrantValidator
    {
        public string GrantType => ExtensionGrantTypes.SMSGrantType;

        public Task ValidateAsync(ExtensionGrantValidationContext context)
        {
            var smsCode = context.Request.Raw.Get("smsCode");
            var phoneNumber = context.Request.Raw.Get("phoneNumber");

            if (string.IsNullOrEmpty(smsCode) || string.IsNullOrEmpty(phoneNumber))
            {
                context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant);
            }
             
            if (phoneNumber == "13488888888" && smsCode == "123456")
            {
                List<Claim> claimList = new List<Claim>();
                claimList.Add(new Claim("userID", "1"));

                context.Result = new GrantValidationResult(
                 subject: phoneNumber,
                 authenticationMethod: ExtensionGrantTypes.SMSGrantType,
                 claims: claimList);
            }
            else
            { 
                context.Result = new GrantValidationResult(
                    TokenRequestErrors.InvalidGrant,
                    "短信碼錯誤!"
                    );
            }
            return Task.FromResult(0);

        }
    }

2、在Startup中注入SMSGrantValidator

services.AddIdentityServer()
                   //配置證書
                   .AddDeveloperSigningCredential()
                   //配置API資源
                   .AddInMemoryApiResources(Config.GetApis())
                   //配置身份資源
                   .AddInMemoryIdentityResources(Config.GetIdentityResources())
                   //預置Client
                   .AddInMemoryClients(Config.GetClients())
                   //自定義登錄返回信息
                   .AddProfileService<ProfileService>()
                   //添加Password模式下用於自定義登錄驗證 
                   .AddResourceOwnerValidator<ResourceOwnerPasswordValidator>()
                   //添加自定義授權模式
                  .AddExtensionGrantValidator<SMSGrantValidator>();

3、配置Client 

//自定義短信驗證模式
                new Client
                {
                    ClientId = "sms",
                    ClientName = "sms",
                    ClientSecrets = { new Secret("123456".Sha256()) },
                    AccessTokenLifetime = 60*60,//單位s
                    AllowedGrantTypes = new[] {ExtensionGrantTypes.SMSGrantType}, //一個 Client 可以配置多個 GrantType
                    SlidingRefreshTokenLifetime =  2592000,
                    AllowOfflineAccess = true,
                    AllowedScopes = new List<string>
                    {
                        "FrameworkAPI",//對應webapi里面的scope配置
                        StandardScopes.OfflineAccess,
                        StandardScopes.OpenId,
                        StandardScopes.Profile
                    }
                }

用postman測試,返回token,測試成功

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM