IdentityServer4 自定义授权模式

Posted fengchao1000

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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,测试成功

技术图片

 

以上是关于IdentityServer4 自定义授权模式的主要内容,如果未能解决你的问题,请参考以下文章

Asp.Net Core 中IdentityServer4 实战之角色授权详解

第十一节:IdentityServer4授权码模式介绍和代码实操演练

IdentityServer4授权模式应用场景

IdentityServer4-客户端的授权模式原理分析

IdentityServer4系列 | 授权码模式

使用IdentityServer4实现一个简单的Oauth2客户端模式授权