前言
上次,我们实现了《ASP.NET Core 自定义认证》:
services.AddAuthentication(option =>
{option.DefaultAuthenticateScheme = DemoAuthenticationOptions.Scheme;option.DefaultChallengeScheme = DemoAuthenticationOptions.Scheme;}).AddDemoAuthentication(options => { });
我们希望同时也能支持 JWT 认证,于是又加上了AddJwtBearer
:
services.AddAuthentication(option =>
{option.DefaultAuthenticateScheme = DemoAuthenticationOptions.Scheme;option.DefaultChallengeScheme = DemoAuthenticationOptions.Scheme;}).AddDemoAuthentication(options => { }).AddJwtBearer(options =>{...});
但是,我们发现 JWT 认证始终不起作用,只能使用自定义认证。
猜测可能是设置了DefaultAuthenticateScheme
的原因,于是去掉了相关代码:
services.AddAuthentication().AddDemoAuthentication(options => { }).AddJwtBearer(options =>{...});
这次问题更大,直接报错了:
查找原因
根据错误提示,我们找到了AuthenticationService
的源码:
public virtual async Task ChallengeAsync(HttpContext context, string? scheme, AuthenticationProperties? properties)
{if (scheme == null){var defaultChallengeScheme = await Schemes.GetDefaultChallengeSchemeAsync();scheme = defaultChallengeScheme?.Name;if (scheme == null){throw new InvalidOperationException($"No authenticationScheme was specified, and there was no DefaultChallengeScheme found. The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action<AuthenticationOptions> configureOptions).");}}
如果没有传入scheme
,则使用DefaultChallengeScheme
。
继续向上查找AuthorizationMiddlewareResultHandler
的源码:
public async Task HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult){if (authorizeResult.Challenged){if (policy.AuthenticationSchemes.Count > 0){foreach (var scheme in policy.AuthenticationSchemes){await context.ChallengeAsync(scheme);}}
scheme
来自于policy.AuthenticationSchemes
。
继续向上查找AuthorizationMiddleware
的源码:
var authorizeData = endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() ?? Array.Empty<IAuthorizeData>();
var policy = await AuthorizationPolicy.CombineAsync(_policyProvider, authorizeData);
policy
来自于IAuthorizeData
的实现类。
那么,IAuthorizeData
的实现类是谁呢:
public class AuthorizeAttribute : Attribute, IAuthorizeData
解决问题
现在,我们只需要在AuthorizeAttribute
设置AuthenticationSchemes
属性即可:
[HttpGet]
[Authorize(AuthenticationSchemes = "Bearer,Demo")]
public string Get()
{...
}
Bearer
和Demo
分别是JwtBearerHandler
和DemoAuthenticationHandler
的默认 scheme。
结论
因为可以为不同方法设置AuthorizeAttribute
,因此,完全可以设置特定路由只能接受指定的认证方式:
[HttpGet("OnlyForBearer")]
[Authorize(AuthenticationSchemes = "Bearer")]
public string OnlyForBearer()
{...
}[HttpGet("OnlyForDemo")]
[Authorize(AuthenticationSchemes = "Demo")]
public string OnlyForDemo()
{...
}
添加微信号【MyIO666】,邀你加入技术交流群