下面是一张ASP.NET Core 3.1 中关于Authentication与Authorization的主流程框线图,点击这里查看全图:https://johnnyqian.net/images/202004/aspnet-core-3.1-request-processing-pipeline.png
一些重要的组件及其源码链接如下:
Component | Source Code |
---|---|
AuthenticationMiddleware | src/Security/Authentication/Core/src/AuthenticationMiddleware.cs |
AuthenticationHandler | src/Security/Authentication/Core/src/AuthenticationHandler.cs |
JwtBearerHandler | src/Security/Authentication/JwtBearer/src/JwtBearerHandler.cs |
RemoteAuthenticationHandler | src/Security/Authentication/Core/src/RemoteAuthenticationHandler.cs |
OAuthHandler | src/Security/Authentication/OAuth/src/OAuthHandler.cs |
MicrosoftAccountHandler | src/Security/Authentication/MicrosoftAccount/src/MicrosoftAccountHandler.cs |
GoogleHandler | src/Security/Authentication/Google/src/GoogleHandler.cs |
FacebookHandler | src/Security/Authentication/Facebook/src/FacebookHandler.cs |
AuthenticationHandlerProvider | src/Http/Authentication.Core/src/AuthenticationHandlerProvider.cs |
AuthenticationService | src/Http/Authentication.Core/src/AuthenticationService.cs |
Component | Source Code |
---|---|
AuthorizationMiddleware | src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs |
AuthorizationHandler | src/Security/Authorization/Core/src/AuthorizationHandler.cs |
DefaultAuthorizationHandlerProvider | src/Security/Authorization/Core/src/DefaultAuthorizationHandlerProvider.cs |
DefaultAuthorizationService | src/Security/Authorization/Core/src/DefaultAuthorizationService.cs |
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
中的如下代码片段确定:
|
这段代码先获取默认的认证Scheme
,接着调用HttpContext
的扩展方法AuthenticateAsync
(该方法实际由IAuthenticationService
来提供)来获取AuthenticateResult
,然后将认证结果中的Principal
赋给HttpContext
的User
属性,最后将请求传递给下一个Middleware(通常是AuthorizationMiddleware
)。
从上面的代码段可以看出,在一个注册了
AuthenticationMiddleware
和AuthorizationMiddleware
的ASP.NET Core项目中,即使Authentication
过程失败了(即context.User
为null
),AuthorizationMiddleware
也是会运行的。
如何判断Authorization
的成功与否?
同样的,Authorization
的成功与否是由AuthorizationMiddleware
来确定的,相关的代码片段如下:
|
这段代码先调用policyEvaluator
的AuthorizeAsync
方法(该方法会调用IAuthorizationService
的AuthorizeAsync
方法)来获取PolicyAuthorizationResult
:
|
|
需要注意的是,在
Authorization
的流程中是需要借助于Authentication
流程的结果以便确定在Authorization
失败后是否返回Challenge(401)还是Forbid(403)。
然后这段代码根据PolicyAuthorizationResult
来调用HttpContext
的扩展方法ChallengeAsync
或ForbidAsync
,并终止整个请求处理流程;或者将请求传递给下一个Middleware(通常是EndpointRoutingMiddleware
,也即是我们常说的MVC Middleware
)。HttpContext
的扩展方法ChallengeAsync
和ForbidAsync
是由IAuthenticationService
来提供的。
我们再深入到DefaultAuthorizationService
中来了解下多个AuthorizationHandler
是如何一起工作的:
|
从上述代码可以看到,在InvokeHandlersAfterFailure
为true
的情况下(默认为true
),所有注册了的AuthorizationHandler
都会被执行。接着,代码调用IAuthorizationEvaluator
中的Evaluate
方法对整个授权流程进行评估,本质上是检查AuthorizationHandlerContext
中的HasSucceeded
属性,其代码如下:
|
可以看到,授权成功的条件是:
基本原则是,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.
没有任何一个AuthorizationHandler
显式调用了context.Fail
至少有一个AuthorizationHandler
显式调用了context.Succeed
PendingRequirements
这个集合为空,也即是所有的requirements都被满足了
[AllowAnonymous]
属性是如何工作的?
[AllowAnonymous]
属性可以绕开(bypass)整个授权流程,即使相应的Controller
或者Action
上有[Authorize]
属性。这也是由AuthorizationMiddleware
中的一段代码来实现的:
|
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 中的管道机制