authorization_一张大图了解ASP.NET Core 3.1中的Authentication与Authorization

(给DotNet加星标,提升.Net技能)

转自:Johnny Qian johnnyqian.net/blog/aspnet-core-3.1-authentication-and-authorization/

下面是一张ASP.NET Core 3.1 中关于Authentication与Authorization的主流程框线图,可以点击放大查看全图。

2680349c44d0c9e97cb5c82cd7dfaa74.png

重要组件

一些重要的组件及其源码链接如下:

Authentication

f9c9b35f00034621657410411d7fd986.png

Authorization

96ae9a246ce08424110073e20b7a566b.png

Middleware与这些Components之间通过一些扩展方法链接起来:

  • src/Http/Authentication.Abstractions/src/AuthenticationHttpContextExtensions.cs

  • src/Security/Authentication/Core/src/AuthAppBuilderExtensions.cs

  • src/Security/Authentication/Core/src/AuthenticationServiceCollectionExtensions.cs

  • src/Http/Authentication.Core/src/AuthenticationCoreServiceCollectionExtensions.cs

  • src/Security/Authorization/Policy/src/AuthorizationAppBuilderExtensions.cs

  • src/Security/Authorization/Policy/src/PolicyServiceCollectionExtensions.cs

  • src/Security/Authorization/Core/src/AuthorizationServiceCollectionExtensions.cs

几点分析

  • 与Authentication和Authorization相关的组件基本是对称存在的,有几个命名不一致。

  • 与AuthenticationHandler相关的具体实现有很多,这是因为认证的逻辑是通用的,可以由外部的身份提供者来完成。

  • 从AuthorizationMiddleware的路径可以看出,ASP.NET Core中的Authorization是基于Policy的。

  • IAuthenticationHandlerProvider中的接口GetHandlerAsync,它是根据authenticationScheme来获取某一个Handler,只要这个Handler认证成功则整个认证流程完成;相对应的IAuthorizationHandlerProvider中的接口GetHandlersAsync则是获取一批Handlers(注意是复数)来共同决定授权的结果。

  • 个人认为有一些Components放置的目录不合理,例如AuthenticationHandlerProvider,AuthenticationService,AuthenticationCoreServiceCollectionExtensions。

  • 基于安全的考虑,ASP.NET Core 不再内置支持Basic Authentication,开发者可以自行编写相应的AuthenticationHandler。

  • 这两个Middleware都是面向接口开发的,借助于DI,开发者可以改变Pipeline的处理逻辑。

几个问题

Authentication的结果存放在哪里?

Authentication的结果由AuthenticationMiddleware中的如下代码片段确定:

var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
if (defaultAuthenticate != null)
{
var result = await context.AuthenticateAsync(defaultAuthenticate.Name);
if (result?.Principal != null)
{
context.User = result.Principal;
}
}
await _next(context);

这段代码先获取默认的认证Scheme,接着调用HttpContext的扩展方法AuthenticateAsync(该方法实际由IAuthenticationService来提供)来获取AuthenticateResult,然后将认证结果中的Principal赋给HttpContext的User属性,最后将请求传递给下一个Middleware(通常是AuthorizationMiddleware)。

从上面的代码段可以看出,在一个注册了AuthenticationMiddleware和AuthorizationMiddleware的ASP.NET Core项目中,即使Authentication过程失败了(即context.User为null),AuthorizationMiddleware也是会运行的。

如何判断Authorization的成功与否?

同样的,Authorization的成功与否是由AuthorizationMiddleware来确定的,相关的代码片段如下:

var authorizeResult =
await policyEvaluator.AuthorizeAsync(policy, authenticateResult, context, resource: endpoint);
if (authorizeResult.Challenged)
{
if (policy.AuthenticationSchemes.Count > 0)
{
foreach (var scheme in policy.AuthenticationSchemes)
{
await context.ChallengeAsync(scheme);
}
}
else
{
await context.ChallengeAsync();
}
return;
}
else if (authorizeResult.Forbidden){
if (policy.AuthenticationSchemes.Count > 0)
{
foreach (var scheme in policy.AuthenticationSchemes)
{
await context.ForbidAsync(scheme);
}
}
else
{
await context.ForbidAsync();
}
return;
}
await _next(context);

这段代码先调用policyEvaluator的AuthorizeAsync方法(该方法会调用IAuthorizationService的AuthorizeAsync方法)来获取PolicyAuthorizationResult:

var result = await _authorization.AuthorizeAsync(context.User, resource, policy);
if (result.Succeeded)
{
return PolicyAuthorizationResult.Success();
}
// If authentication was successful, return forbidden, otherwise challenge
return (authenticationResult.Succeeded)
? PolicyAuthorizationResult.Forbid()
: PolicyAuthorizationResult.Challenge();

需要注意的是,在Authorization的流程中是需要借助于Authentication流程的结果以便确定在Authorization失败后是否返回Challenge(401)还是Forbid(403)。

然后这段代码根据PolicyAuthorizationResult来调用HttpContext的扩展方法ChallengeAsync或ForbidAsync,并终止整个请求处理流程;或者将请求传递给下一个Middleware(通常是EndpointRoutingMiddleware,也即是我们常说的MVC Middleware)。HttpContext的扩展方法ChallengeAsync和ForbidAsync是由IAuthenticationService来提供的。

我们再深入到DefaultAuthorizationService中来了解下多个AuthorizationHandler是如何一起工作的:

var authContext = _contextFactory.CreateContext(requirements, user, resource);
var handlers = await _handlers.GetHandlersAsync(authContext);
foreach (var handler in handlers)
{
await handler.HandleAsync(authContext);
if (!_options.InvokeHandlersAfterFailure && authContext.HasFailed)
{
break;
}
}
var result = _evaluator.Evaluate(authContext);

从上述代码可以看到,在InvokeHandlersAfterFailure为true的情况下(默认为true),所有注册了的AuthorizationHandler都会被执行。

接着,代码调用IAuthorizationEvaluator中的Evaluate方法对整个授权流程进行评估,本质上是检查AuthorizationHandlerContext中的HasSucceeded属性,其代码如下:

/// 
/// Flag indicating whether the current authorization processing has succeeded.
///
public virtual bool HasSucceeded
{
get
{
return !_failCalled && _succeedCalled && !PendingRequirements.Any();
}
}

可以看到,授权成功的条件是:

  • 没有任何一个AuthorizationHandler显式调用了context.Fail

  • 至少有一个AuthorizationHandler显式调用了context.Succeed

  • PendingRequirements这个集合为空,也即是所有的requirements都被满足了

基本原则是,AuthorizationHandler一般不需要显式调用context.Fail,除非开发者认为某一个requirement必须被当前的AuthorizationHandler满足。针对同一个requirement,其它的AuthorizationHandler可能会依据某些条件认为该requirement是满足的。

这里有个问题是,如果某一个AuthorizationHandler显式调用了context.Fail,那么整个授权流程的结果就是失败的。

那么此时为什么还要继续执行其它的AuthorizationHandler(InvokeHandlersAfterFailure默认为true),而不是快速失败(fail-fast)?官方文档给出的解释如下:

If a handler calls context.Succeed or context.Fail, all other handlers are still called. This allows requirements to produce side effects, such as logging, which takes place even if another handler has successfully validated or failed a requirement. When set to false, the InvokeHandlersAfterFailure property (available in ASP.NET Core 1.1 and later) short-circuits the execution of handlers when context.Fail is called. InvokeHandlersAfterFailure defaults to true, in which case all handlers are called.

[AllowAnonymous]属性是如何工作的?

[AllowAnonymous]属性可以绕开(bypass)整个授权流程,即使相应的Controller或者Action上有[Authorize]属性。这也是由AuthorizationMiddleware中的一段代码来实现的:

// Allow Anonymous skips all authorization
if (endpoint?.Metadata.GetMetadata() != null)
{await _next(context);return;
}

参考文章

  • ASP.NET Core Middleware

  • ASP.NET Core authentication

  • ASP.NET Core authorization

  • Policy-based authorization in ASP.NET Core

  • ASP.NET Core - Middleware

  • ASP.NET Core in Action - What is middleware?

  • ASP.NET Core middleware and authorization

  • ASP.NET Core 中的那些认证中间件及一些重要知识点

  • ASP.NET Core 中的管道机制

推荐阅读   点击标题可跳转 一起读源码走进C#并发队列的内部世界 .NET微服务实践:微服务框架选型 ASP.NET Core Kestrel免费实现https

看完本文有收获?请转发分享给更多人

关注「DotNet」加星标,提升.Net技能 

82c2a97b71424a89e7b3e167c5e83ecf.png

好文章,我在看❤️

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值