IdentityServer4除了提供常规的几种授权模式外(AuthorizationCode、ClientCredentials、Password、RefreshToken、DeviceCode),还提供了可以拓展的授权模式,下面就根据源码简单说下IdentityServer4是如何实现自定义授权模式的。
一、查看IdentityServer4自定义授权模式源码
当用户请求 connect/token 地址时,会执行TokenRequestValidator类的ValidateRequestAsync方法,在ValidateRequestAsync方法中根据GrantTypes类型调用不同的Validate方法,如下:
public async TaskValidateRequestAsync(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_validators;//可以注入多个自定义授权类,用集合保存 /// /// Initializes a new instance of the class. /// /// The validators. /// The logger. public ExtensionGrantValidator(IEnumerable validators, ILogger logger) { if (validators == null) { _validators = Enumerable.Empty (); } else { //把注入的自定义授权类放入集合中 _validators = validators; } _logger = logger; } /// /// 得到所有可用的自定义授权类型 /// /// public IEnumerable<string> GetAvailableGrantTypes() { return _validators.Select(v => v.GrantType); } /// /// Validates the request. /// /// The request. /// public async Task 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") { ListclaimList = new List (); 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() //添加Password模式下用于自定义登录验证 .AddResourceOwnerValidator () //添加自定义授权模式 .AddExtensionGrantValidator ();
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,测试成功