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,測試成功