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(, 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 == "" && smsCode == "")
{
List<Claim> claimList = new List<Claim>();
claimList.Add(new Claim("userID", "")); context.Result = new GrantValidationResult(
subject: phoneNumber,
authenticationMethod: ExtensionGrantTypes.SMSGrantType,
claims: claimList);
}
else
{
context.Result = new GrantValidationResult(
TokenRequestErrors.InvalidGrant,
"短信码错误!"
);
}
return Task.FromResult(); }
}
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("".Sha256()) },
AccessTokenLifetime = *,//单位s
AllowedGrantTypes = new[] {ExtensionGrantTypes.SMSGrantType}, //一个 Client 可以配置多个 GrantType
SlidingRefreshTokenLifetime = ,
AllowOfflineAccess = true,
AllowedScopes = new List<string>
{
"FrameworkAPI",//对应webapi里面的scope配置
StandardScopes.OfflineAccess,
StandardScopes.OpenId,
StandardScopes.Profile
}
}
用postman测试,返回token,测试成功