基于.NetCore3.1系列 —— 认证授权方案之授权揭秘 (下篇)
2021-01-23 15:13
标签:requests VID bho logs oca rop 特定 设计 lan 回顾:基于.NetCore3.1系列 —— 认证授权方案之授权揭秘 (上篇) 在上一篇中,主要讲解了授权在配置方面的源码,从添加授权配置开始,我们引入了需要的授权配置选项,而不同的授权要求构建不同的策略方式,从而实现一种自己满意的授权需求配置要求。 在这一节中,继续上一篇的内容往下深入了解授权内部机制的奥秘以及是如何实现执行授权流程的。 在上一篇中,我们通过定义授权策略,查看源码发现,在对授权配置 之前我们进行对步骤一的授权有了大概了解,所以下面我们将对步骤二进行的注册对象进行说明。 授权服务接口,用来确定授权是否成功的主要服务,接口的定义为 两个接口的参数不同之处在于 同时asp.net core还为 接口的默认实现为DefaultAuthorizationService 通过上面的代码可以发现,在对象实例中,通过构造函数的方式分别注入了 这里就用到了注入的几个核心对象来实现完成授权的。下面会分别介绍到的。 由上面的 再加上在使用 由上面的代码可以看出,在实现 在上一章中介绍过,我们定义的策略都保存在AuthorizationOptions的中 先看看这个接口的源代码 接口定义了一个唯一的方法 再来看看 因此,在下面我们刚好会提到了 这个是接口的方法,作用是获取所有的授权Handler 根据之前提到的授权上下文作为 默认接口的实现为 从默认实现的方式可以看出,利用构造函数的方式注入默认的 这个时候,你可能会问,那么 对应下面的 IAuthorizationHandler 由 由默认实现可以看出, 其中的 这里的两个授权结果 正是 接口方式实现,判断是否授权,实现此接口的类 如果允许授权,可通过此接口的方法来决定是否允许授权。 之前我们还介绍到,我们定义的Requirement,可以直接实现 在默认的AuthorizationHandlerProvider中,会从DI系统中获取到我们注册的所有Handler,最终调用其 我们在实现 如上,首先会在 那我们定义的直接实现 我们可以发现, 它负责调用该策略中所有实现了 所以可以看到的出, 接口的方式实现,为特定需求类型调用的授权处理程序的基类 定义了两个方法 授权中间件委托它来实现身份验证和授权处理,它内部会调用AuthorizationService,进而执行所有授权处理器AuthorizationHandler, (在后面会提到授权中间件用到这两个方法) 当授权策略没有设置AuthenticationSchemes,则只判断下当前请求是否已做身份验证,若做了就返回成功 其中 当我们希望使用非默认的Scheme,或者是想合并多个认证Scheme的Claims时,就需要使用基于Scheme的授权来重置Claims了。 它的实现也很简单,直接使用我们在授权策略中指定的Schemes来依次调用认证服务的 该方法会根据Requirements来完成授权,具体的实现是通过调用 最终返回的是一个 以上汇总 在Configure中注册管道:运行使用调用方法来配置Http请求管道 在这里使用了授权中间件来检查授权,来看看中间件的源码 进行代码分解: 当授权评估拒绝就直接调用身份验证方案进行拒绝。 整个过程中,授权中间件会调用授权服务 基于.NetCore3.1系列 —— 认证授权方案之授权揭秘 (下篇) 标签:requests VID bho logs oca rop 特定 设计 lan 原文地址:https://www.cnblogs.com/i3yuan/p/13275774.html一、前言
二、说明
AuthorizationOptions
之后,授权系统通过DI的方式注册了几个核心的默认实现。三、开始
3.1 IAuthorizationService
public interface IAuthorizationService
{
Task AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable
IAuthorizationRequirement
和policyName
,分别是指定资源的一组特定要求和指定的授权名称。IAuthorizationService
接口拓展了几个方法: public static class AuthorizationServiceExtensions
{
public static Task AuthorizeAsync(this IAuthorizationService service, ClaimsPrincipal user, object resource, IAuthorizationRequirement requirement)
{
if (service == null)
{
throw new ArgumentNullException(nameof(service));
}
if (requirement == null)
{
throw new ArgumentNullException(nameof(requirement));
}
return service.AuthorizeAsync(user, resource, new IAuthorizationRequirement[] { requirement });
}
public static Task AuthorizeAsync(this IAuthorizationService service, ClaimsPrincipal user, object resource, AuthorizationPolicy policy)
{
if (service == null)
{
throw new ArgumentNullException(nameof(service));
}
if (policy == null)
{
throw new ArgumentNullException(nameof(policy));
}
return service.AuthorizeAsync(user, resource, policy.Requirements);
}
public static Task AuthorizeAsync(this IAuthorizationService service, ClaimsPrincipal user, AuthorizationPolicy policy)
{
if (service == null)
{
throw new ArgumentNullException(nameof(service));
}
if (policy == null)
{
throw new ArgumentNullException(nameof(policy));
}
return service.AuthorizeAsync(user, resource: null, policy: policy);
}
public static Task AuthorizeAsync(this IAuthorizationService service, ClaimsPrincipal user, string policyName)
{
if (service == null)
{
throw new ArgumentNullException(nameof(service));
}
if (policyName == null)
{
throw new ArgumentNullException(nameof(policyName));
}
return service.AuthorizeAsync(user, resource: null, policyName: policyName);
}
}
DefaultAuthorizationService
的实现主要是用来对 IAuthorizationRequirement对象的授权检验。public class DefaultAuthorizationService : IAuthorizationService
{
private readonly AuthorizationOptions _options;
private readonly IAuthorizationHandlerContextFactory _contextFactory;
private readonly IAuthorizationHandlerProvider _handlers;
private readonly IAuthorizationEvaluator _evaluator;
private readonly IAuthorizationPolicyProvider _policyProvider;
private readonly ILogger _logger;
public DefaultAuthorizationService(IAuthorizationPolicyProvider policyProvider, IAuthorizationHandlerProvider handlers, ILogger
IAuthorizationPolicyProvider
、IAuthorizationHandlerProvider
、IAuthorizationEvaluator
、IAuthorizationHandlerContextFactory
这几个核心服务,以及配置选项的AuthorizationOptions对象,再通过实现的方法AuthorizeAsync
可以看出,在方法中调用GetPolicyAsync
来获取Requirements
,具体的可以看一下上一节的AuthorizationPolicy,而后在根据授权上下文来判断。3.2 IAuthorizationPolicyProvider
IAuthorizationServer
接口的默认实现可以发现,在进行授权检验的时候,DefaultAuthorizationService
会利用注入的IAuthorizationPolicyProvider
服务来提供注册的授权策略,所以我们查看源码发现,接口提供 了默认的授权策略GetDefaultPolicyAsync
和指定名称的授权策略·GetPolicyAsync(string policyName)
的方法。public interface IAuthorizationPolicyProvider
{
Task GetPolicyAsync(string policyName);
Task GetDefaultPolicyAsync();
Task GetFallbackPolicyAsync();
}
[Authorize]
进行策略授权的时候,会根据提供的接口方法来获取指定的授权策略。IAuthorizationPolicyProvider
来根据名称获取到策略对象,默认实现为DefaultAuthorizationPolicyProvider
:DefaultAuthorizationPolicyProvider
public class DefaultAuthorizationPolicyProvider : IAuthorizationPolicyProvider
{
private readonly AuthorizationOptions _options;
private Task _cachedDefaultPolicy;
private Task _cachedFallbackPolicy;
public DefaultAuthorizationPolicyProvider(IOptions options)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
_options = options.Value;
}
public Task GetDefaultPolicyAsync()
{
return GetCachedPolicy(ref _cachedDefaultPolicy, _options.DefaultPolicy);
}
public Task GetFallbackPolicyAsync()
{
return GetCachedPolicy(ref _cachedFallbackPolicy, _options.FallbackPolicy);
}
private Task GetCachedPolicy(ref Task cachedPolicy, AuthorizationPolicy currentPolicy)
{
var local = cachedPolicy;
if (local == null || local.Result != currentPolicy)
{
cachedPolicy = local = Task.FromResult(currentPolicy);
}
return local;
}
public virtual Task GetPolicyAsync(string policyName)
{
return Task.FromResult(_options.GetPolicy(policyName));
}
}
DefaultAuthorizationPolicyProvider
对象进行构造函数的方式注入了IOptions options
服务来提供配置选项AuthorizationOptions
(不懂的可以查看上一篇的AuthorizationOptions),再通过实现的方法可以看出是如何获取到注册的授权策略的了。附加一个图片PolicyMap
字典中,由上代码可以发现这字典的用处。3.3 IAuthorizationHandlerContextFactory
public interface IAuthorizationHandlerContextFactory
{
AuthorizationHandlerContext CreateContext(IEnumerable
CreateContext
,作用在于创建授权上下文AuthorizationHandlerContext
对象。接口默认实现方式 public class DefaultAuthorizationHandlerContextFactory : IAuthorizationHandlerContextFactory
{
public virtual AuthorizationHandlerContext CreateContext(IEnumerable
AuthorizationHandlerContext
授权上下文对象,可以看出,上下文中主要包括用户的Claims和授权策略的要求Requirementspublic class AuthorizationHandlerContext
{
private HashSet
IAuthorizationHandlerProvider
中的方法,可以根据授权上下文获取到请求调用的处理程序。3.4 IAuthorizationHandlerProvider
public interface IAuthorizationHandlerProvider
{
Task
GetHandlersAsync
方法参数对象来提取IAuthorizationHandler
对象。DefaultAuthorizationHandlerProvider
, 处理程序的默认实现,为授权请求提供IAuthorizationHandler
public class DefaultAuthorizationHandlerProvider : IAuthorizationHandlerProvider
{
private readonly IEnumerable
IAuthorizationHandler
的对象,但是我们再看看接口的实现方法可以发现,GetHandlersAsync
返回的IAuthorizationHandler
对象并不是从给定的AuthorizationHandlerContext
上下文中获取的,而是直接通过构造函数的方式注入得到的。IAuthorizationHandler
是在哪里注入的呢?3.5 IAuthorizationEvaluator
DefaultAuthorizationService
中的授权方法过程调用了 var result = _evaluator.Evaluate(authContext);
IAuthorizationEvaluator
接口,来确定授权结果是否成功。 public interface IAuthorizationEvaluator
{
AuthorizationResult Evaluate(AuthorizationHandlerContext context);
}
IAuthorizationEvaluator
的唯一方法Evaluate
,该方法会根据之前提供的授权上下文返回一个表示授权成功的AuthorizationResult
对象。默认实现为DefaultAuthorizationEvaluator
public class DefaultAuthorizationEvaluator : IAuthorizationEvaluator
{
public AuthorizationResult Evaluate(AuthorizationHandlerContext context)
=> context.HasSucceeded
? AuthorizationResult.Success()
: AuthorizationResult.Failed(context.HasFailed
? AuthorizationFailure.ExplicitFail()
: AuthorizationFailure.Failed(context.PendingRequirements));
}
AuthorizationHandlerContext
对象的HasSucceeded
属性决定了授权是否成功。当验证通过时,授权上下文中的HasSucceeded
才会为True。AuthorizationResult
和AuthorizationFailure
分别为public class AuthorizationResult
{
private AuthorizationResult() { }
public bool Succeeded { get; private set; }
public AuthorizationFailure Failure { get; private set; }
public static AuthorizationResult Success() => new AuthorizationResult { Succeeded = true };
public static AuthorizationResult Failed(AuthorizationFailure failure) => new AuthorizationResult { Failure = failure };
public static AuthorizationResult Failed() => new AuthorizationResult { Failure = AuthorizationFailure.ExplicitFail() };
}
public class AuthorizationFailure
{
private AuthorizationFailure() { }
public bool FailCalled { get; private set; }
public IEnumerable
IAuthorizationService
进行实现授权AuthorizeAsync
来完成校验返回的结果。3.6 IAuthorizationHandler
public interface IAuthorizationHandler
{
Task HandleAsync(AuthorizationHandlerContext context);
}
IAuthorizationHandler
接口,也可以单独定义Handler,但是需要注册到DI系统中去。HandleAsync
方法。IAuthorizationHandler
接口时,通常是继承自AuthorizationHandler
来实现,它有如下定义:public abstract class AuthorizationHandler
HandleAsync
过滤出与Requirement对匹配的Handler,然后再调用其HandleRequirementAsync
方法。IAuthorizationHandler
了接口的Requirement又是如何执行的呢?IAuthorizationHandler
在AddAuthorization
拓展方法中可以看到默认注册了一个PassThroughAuthorizationHandler
默认实现为:public class PassThroughAuthorizationHandler : IAuthorizationHandler
{
public async Task HandleAsync(AuthorizationHandlerContext context)
{
foreach (var handler in context.Requirements.OfType
IAuthorizationHandler
接口的Requirement
。通过接口实现的方法可以看出,当PassThroughAuthorizationHandler
对象的HandleAsync
方法被执行的时候,它会从AuthroizationHanderContext
的Requirements
属性中提取所有的IAuthoizationHandler
对象,并逐个调用它们的HandleAsync
方法来实施授权检验。PassThroughAuthorizationHandler
是一个特殊并且重要的授权处理器类型,其特殊之处在于它并没有实现针对某个具体规则的授权检验,但是AuthorizationHandlerContext上下文所有的IAuthorizationHandler
都是通过该对象驱动执行的。3.7 IPolicyEvaluator
public interface IPolicyEvaluator
{
Task AuthenticateAsync(AuthorizationPolicy policy, HttpContext context);
Task
AuthenticateAsync
和AuthorizeAsync
方法IPolicyEvaluator
的默认实现为PolicyEvaluator
public class PolicyEvaluator : IPolicyEvaluator
{
private readonly IAuthorizationService _authorization;
public PolicyEvaluator(IAuthorizationService authorization)
{
_authorization = authorization;
}
public virtual async Task AuthenticateAsync(AuthorizationPolicy policy, HttpContext context)
{
if (policy.AuthenticationSchemes != null && policy.AuthenticationSchemes.Count > 0)
{
ClaimsPrincipal newPrincipal = null;
foreach (var scheme in policy.AuthenticationSchemes)
{
var result = await context.AuthenticateAsync(scheme);
if (result != null && result.Succeeded)
{
newPrincipal = SecurityHelper.MergeUserPrincipal(newPrincipal, result.Principal);
}
}
if (newPrincipal != null)
{
context.User = newPrincipal;
return AuthenticateResult.Success(new AuthenticationTicket(newPrincipal, string.Join(";", policy.AuthenticationSchemes)));
}
else
{
context.User = new ClaimsPrincipal(new ClaimsIdentity());
return AuthenticateResult.NoResult();
}
}
return (context.User?.Identity?.IsAuthenticated ?? false)
? AuthenticateResult.Success(new AuthenticationTicket(context.User, "context.User"))
: AuthenticateResult.NoResult();
}
public virtual async Task
3.7.1、AuthenticateAsync
当授权策略设置了AuthenticationSchemes,则遍历身份验证方案逐个进行身份验证处理 。context.User
就是使用context.AuthenticateAsync(DefaultAuthenticateScheme)
来赋值的,将所有得到的用户标识重组成一个复合的用户标识。
AuthenticateAsync
方法,并将生成的Claims合并,最后返回我们熟悉的AuthenticateResult
认证结果。3.7.2、AuthorizeAsync
IAuthorizationService
调用AuthorizeAsync
来实现的。PolicyAuthorizationResult
对象,并在授权失败时,根据认证结果来返回Forbid(未授权)
或Challenge(未登录)
。
IAuthorizationPolicyProvider
来获取指定名称的授权。四、中间件
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{ app.UseRouting();
//开启认证授权
app.UseAuthentication();
app.UseAuthorization();
}
AuthorizationMiddleware
public class AuthorizationMiddleware
{
// Property key is used by Endpoint routing to determine if Authorization has run
private const string AuthorizationMiddlewareInvokedWithEndpointKey = "__AuthorizationMiddlewareWithEndpointInvoked";
private static readonly object AuthorizationMiddlewareWithEndpointInvokedValue = new object();
private readonly RequestDelegate _next;
private readonly IAuthorizationPolicyProvider _policyProvider;
public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvider policyProvider)
{
_next = next ?? throw new ArgumentNullException(nameof(next));
_policyProvider = policyProvider ?? throw new ArgumentNullException(nameof(policyProvider));
}
public async Task Invoke(HttpContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var endpoint = context.GetEndpoint();
if (endpoint != null)
{
context.Items[AuthorizationMiddlewareInvokedWithEndpointKey] = AuthorizationMiddlewareWithEndpointInvokedValue;
}
var authorizeData = endpoint?.Metadata.GetOrderedMetadata
var endpoint = context.GetEndpoint();
endpoint
的时候,会通过终结点拿到关联的IAuthorizeData
集合var authorizeData = endpoint?.Metadata.GetOrderedMetadata
IAuthorizeData
集合调用AuthorizationPolicy.CombineAsync()来创建组合策略(具体了可以看一下上一章) ( 用例: [Authorize(Policy = "BaseRole")] )var policy = await AuthorizationPolicy.CombineAsync(_policyProvider, authorizeData);
IPolicyEvaluator
获取策略评估器对得到的组合策略进行身份验证,多种身份验证得到的用户证件信息会合并进HttpContext.User
var policyEvaluator = context.RequestServices.GetRequiredService
[AllowAnonymous]
的时候,则直接跳过授权检验。 if (endpoint?.Metadata.GetMetadata
IPolicyEvaluator
提供的AuthorizeAsync
授权检查方法,进行策略授权检查。 var authorizeResult = await policyEvaluator.AuthorizeAsync(policy, authenticateResult, context, resource: endpoint);
if (authorizeResult.Challenged)
{
if (policy.AuthenticationSchemes.Any())
{
foreach (var scheme in policy.AuthenticationSchemes)
{
await context.ChallengeAsync(scheme);
}
}
else
{
await context.ChallengeAsync();
}
return;
}
else if (authorizeResult.Forbidden)
{
if (policy.AuthenticationSchemes.Any())
{
foreach (var scheme in policy.AuthenticationSchemes)
{
await context.ForbidAsync(scheme);
}
}
else
{
await context.ForbidAsync();
}
return;
}
IAuthorizationService
来进行授权处理五、总结
IAuthorizationService
来实现的,而我们进行使用只需要提供授权策略的Requirement,非常方便灵活的使用。
下一篇:每周一题:替换空格(已更新JS)
文章标题:基于.NetCore3.1系列 —— 认证授权方案之授权揭秘 (下篇)
文章链接:http://soscw.com/index.php/essay/45935.html