一、前言

回顾:基于.NetCore3.1系列 —— 认证授权方案之授权揭秘 (上篇)

在上一篇中,主要讲解了授权在配置方面的源码,从添加授权配置开始,我们引入了需要的授权配置选项,而不同的授权要求构建不同的策略方式,从而实现一种自己满意的授权需求配置要求。

在这一节中,继续上一篇的内容往下深入了解授权内部机制的奥秘以及是如何实现执行授权流程的。

二、说明

在上一篇中,我们通过定义授权策略,查看源码发现,在对授权配置AuthorizationOptions之后,授权系统通过DI的方式注册了几个核心的默认实现。

之前我们进行对步骤一的授权有了大概了解,所以下面我们将对步骤二进行的注册对象进行说明。

三、开始

3.1 IAuthorizationService

授权服务接口,用来确定授权是否成功的主要服务,接口的定义为

    public interface IAuthorizationService
{
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements);
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName);
}

两个接口的参数不同之处在于IAuthorizationRequirementpolicyName,分别是指定资源的一组特定要求和指定的授权名称。

同时asp.net core还为IAuthorizationService 接口拓展了几个方法:

    public static class AuthorizationServiceExtensions
{
public static Task<AuthorizationResult> 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<AuthorizationResult> 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<AuthorizationResult> 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<AuthorizationResult> 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

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<DefaultAuthorizationService> logger, IAuthorizationHandlerContextFactory contextFactory, IAuthorizationEvaluator evaluator, IOptions<AuthorizationOptions> options)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
if (policyProvider == null)
{
throw new ArgumentNullException(nameof(policyProvider));
}
if (handlers == null)
{
throw new ArgumentNullException(nameof(handlers));
}
if (logger == null)
{
throw new ArgumentNullException(nameof(logger));
}
if (contextFactory == null)
{
throw new ArgumentNullException(nameof(contextFactory));
}
if (evaluator == null)
{
throw new ArgumentNullException(nameof(evaluator));
} _options = options.Value;
_handlers = handlers;
_policyProvider = policyProvider;
_logger = logger;
_evaluator = evaluator;
_contextFactory = contextFactory;
} public async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements)
{
if (requirements == null)
{
throw new ArgumentNullException(nameof(requirements));
} 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);
if (result.Succeeded)
{
_logger.UserAuthorizationSucceeded();
}
else
{
_logger.UserAuthorizationFailed();
}
return result;
} public async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName)
{
if (policyName == null)
{
throw new ArgumentNullException(nameof(policyName));
} var policy = await _policyProvider.GetPolicyAsync(policyName);
if (policy == null)
{
throw new InvalidOperationException($"No policy found: {policyName}.");
}
return await this.AuthorizeAsync(user, resource, policy);
}
}

通过上面的代码可以发现,在对象实例中,通过构造函数的方式分别注入了IAuthorizationPolicyProviderIAuthorizationHandlerProviderIAuthorizationEvaluatorIAuthorizationHandlerContextFactory这几个核心服务,以及配置选项的AuthorizationOptions对象,再通过实现的方法AuthorizeAsync可以看出,在方法中调用GetPolicyAsync来获取Requirements,具体的可以看一下上一节的AuthorizationPolicy,而后在根据授权上下文来判断。

这里就用到了注入的几个核心对象来实现完成授权的。下面会分别介绍到的。

3.2 IAuthorizationPolicyProvider

由上面的IAuthorizationServer接口的默认实现可以发现,在进行授权检验的时候,DefaultAuthorizationService会利用注入的IAuthorizationPolicyProvider服务来提供注册的授权策略,所以我们查看源码发现,接口提供 了默认的授权策略GetDefaultPolicyAsync和指定名称的授权策略·GetPolicyAsync(string policyName)的方法。

public interface IAuthorizationPolicyProvider
{
Task<AuthorizationPolicy> GetPolicyAsync(string policyName);
Task<AuthorizationPolicy> GetDefaultPolicyAsync();
Task<AuthorizationPolicy> GetFallbackPolicyAsync();
}

再加上在使用[Authorize]进行策略授权的时候,会根据提供的接口方法来获取指定的授权策略。

IAuthorizationPolicyProvider来根据名称获取到策略对象,默认实现为DefaultAuthorizationPolicyProvider

DefaultAuthorizationPolicyProvider

    public class DefaultAuthorizationPolicyProvider : IAuthorizationPolicyProvider
{
private readonly AuthorizationOptions _options;
private Task<AuthorizationPolicy> _cachedDefaultPolicy;
private Task<AuthorizationPolicy> _cachedFallbackPolicy; public DefaultAuthorizationPolicyProvider(IOptions<AuthorizationOptions> options)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
} _options = options.Value;
} public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
{
return GetCachedPolicy(ref _cachedDefaultPolicy, _options.DefaultPolicy);
} public Task<AuthorizationPolicy> GetFallbackPolicyAsync()
{
return GetCachedPolicy(ref _cachedFallbackPolicy, _options.FallbackPolicy);
} private Task<AuthorizationPolicy> GetCachedPolicy(ref Task<AuthorizationPolicy> cachedPolicy, AuthorizationPolicy currentPolicy)
{
var local = cachedPolicy;
if (local == null || local.Result != currentPolicy)
{
cachedPolicy = local = Task.FromResult(currentPolicy);
}
return local;
} public virtual Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
{
return Task.FromResult(_options.GetPolicy(policyName));
}
}

由上面的代码可以看出,在实现DefaultAuthorizationPolicyProvider对象进行构造函数的方式注入了IOptions<AuthorizationOptions> options服务来提供配置选项AuthorizationOptions(不懂的可以查看上一篇的AuthorizationOptions),再通过实现的方法可以看出是如何获取到注册的授权策略的了。附加一个图片

在上一章中介绍过,我们定义的策略都保存在AuthorizationOptions的中PolicyMap字典中,由上代码可以发现这字典的用处。

3.3 IAuthorizationHandlerContextFactory

先看看这个接口的源代码

public interface IAuthorizationHandlerContextFactory
{
AuthorizationHandlerContext CreateContext(IEnumerable<IAuthorizationRequirement> requirements, ClaimsPrincipal user, object resource);
}

接口定义了一个唯一的方法CreateContext,作用在于创建授权上下文AuthorizationHandlerContext对象。接口默认实现方式

    public class DefaultAuthorizationHandlerContextFactory : IAuthorizationHandlerContextFactory
{
public virtual AuthorizationHandlerContext CreateContext(IEnumerable<IAuthorizationRequirement> requirements, ClaimsPrincipal user, object resource)
{
return new AuthorizationHandlerContext(requirements, user, resource);
}
}

再来看看AuthorizationHandlerContext授权上下文对象,可以看出,上下文中主要包括用户的Claims和授权策略的要求Requirements

public class AuthorizationHandlerContext
{
private HashSet<IAuthorizationRequirement> _pendingRequirements;
private bool _failCalled;
private bool _succeedCalled;
public AuthorizationHandlerContext(
IEnumerable<IAuthorizationRequirement> requirements,
ClaimsPrincipal user,
object resource)
{
if (requirements == null)
{
throw new ArgumentNullException(nameof(requirements));
} Requirements = requirements;
User = user;
Resource = resource;
_pendingRequirements = new HashSet<IAuthorizationRequirement>(requirements);
} public virtual IEnumerable<IAuthorizationRequirement> Requirements { get; } public virtual ClaimsPrincipal User { get; } public virtual object Resource { get; } public virtual IEnumerable<IAuthorizationRequirement> PendingRequirements { get { return _pendingRequirements; } } public virtual bool HasFailed { get { return _failCalled; } } public virtual bool HasSucceeded
{
get
{
return !_failCalled && _succeedCalled && !PendingRequirements.Any();
}
} public virtual void Fail()
{
_failCalled = true;
}
public virtual void Succeed(IAuthorizationRequirement requirement)
{
_succeedCalled = true;
_pendingRequirements.Remove(requirement);
}
}

因此,在下面我们刚好会提到了IAuthorizationHandlerProvider 中的方法,可以根据授权上下文获取到请求调用的处理程序。

3.4 IAuthorizationHandlerProvider

这个是接口的方法,作用是获取所有的授权Handler

public interface IAuthorizationHandlerProvider
{
Task<IEnumerable<IAuthorizationHandler>> GetHandlersAsync(AuthorizationHandlerContext context);
}

根据之前提到的授权上下文作为GetHandlersAsync方法参数对象来提取IAuthorizationHandler对象。

默认接口的实现为DefaultAuthorizationHandlerProvider, 处理程序的默认实现,为授权请求提供IAuthorizationHandler

    public class DefaultAuthorizationHandlerProvider : IAuthorizationHandlerProvider
{
private readonly IEnumerable<IAuthorizationHandler> _handlers; public DefaultAuthorizationHandlerProvider(IEnumerable<IAuthorizationHandler> handlers)
{
if (handlers == null)
{
throw new ArgumentNullException(nameof(handlers));
} _handlers = handlers;
} public Task<IEnumerable<IAuthorizationHandler>> GetHandlersAsync(AuthorizationHandlerContext context)
=> Task.FromResult(_handlers);
}

从默认实现的方式可以看出,利用构造函数的方式注入默认的IAuthorizationHandler的对象,但是我们再看看接口的实现方法可以发现,GetHandlersAsync返回的IAuthorizationHandler对象并不是从给定的AuthorizationHandlerContext上下文中获取的,而是直接通过构造函数的方式注入得到的。

这个时候,你可能会问,那么IAuthorizationHandler是在哪里注入的呢?

对应下面的 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。

其中的AuthorizationResultAuthorizationFailure分别为

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<IAuthorizationRequirement> FailedRequirements { get;private set; } public static AuthorizationFailure ExplicitFail()
=> new AuthorizationFailure
{
FailCalled = true,
FailedRequirements = new IAuthorizationRequirement[0]
}; public static AuthorizationFailure Failed(IEnumerable<IAuthorizationRequirement> failed)
=> new AuthorizationFailure { FailedRequirements = failed };
}

这里的两个授权结果 正是IAuthorizationService 进行实现授权AuthorizeAsync来完成校验返回的结果。

3.6 IAuthorizationHandler

接口方式实现,判断是否授权,实现此接口的类

public interface IAuthorizationHandler
{
Task HandleAsync(AuthorizationHandlerContext context);
}

如果允许授权,可通过此接口的方法来决定是否允许授权。

之前我们还介绍到,我们定义的Requirement,可以直接实现IAuthorizationHandler接口,也可以单独定义Handler,但是需要注册到DI系统中去。

在默认的AuthorizationHandlerProvider中,会从DI系统中获取到我们注册的所有Handler,最终调用其HandleAsync方法。

我们在实现IAuthorizationHandler接口时,通常是继承自AuthorizationHandler来实现,它有如下定义:

public abstract class AuthorizationHandler<TRequirement> : IAuthorizationHandler where TRequirement : IAuthorizationRequirement
{
public virtual async Task HandleAsync(AuthorizationHandlerContext context)
{
foreach (var req in context.Requirements.OfType<TRequirement>())
{
await HandleRequirementAsync(context, req);
}
} protected abstract Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement);
}

如上,首先会在HandleAsync过滤出与Requirement对匹配的Handler,然后再调用其HandleRequirementAsync方法。

那我们定义的直接实现IAuthorizationHandler了接口的Requirement又是如何执行的呢?

我们可以发现,IAuthorizationHandlerAddAuthorization拓展方法中可以看到默认注册了一个PassThroughAuthorizationHandler默认实现为:

public class PassThroughAuthorizationHandler : IAuthorizationHandler
{
public async Task HandleAsync(AuthorizationHandlerContext context)
{
foreach (var handler in context.Requirements.OfType<IAuthorizationHandler>())
{
await handler.HandleAsync(context);
}
}
}

它负责调用该策略中所有实现了IAuthorizationHandler接口的Requirement。通过接口实现的方法可以看出,当PassThroughAuthorizationHandler对象的HandleAsync方法被执行的时候,它会从AuthroizationHanderContextRequirements属性中提取所有的IAuthoizationHandler对象,并逐个调用它们的HandleAsync方法来实施授权检验。

所以可以看到的出,PassThroughAuthorizationHandler是一个特殊并且重要的授权处理器类型,其特殊之处在于它并没有实现针对某个具体规则的授权检验,但是AuthorizationHandlerContext上下文所有的IAuthorizationHandler都是通过该对象驱动执行的。

3.7 IPolicyEvaluator

接口的方式实现,为特定需求类型调用的授权处理程序的基类

    public interface IPolicyEvaluator
{
Task<AuthenticateResult> AuthenticateAsync(AuthorizationPolicy policy, HttpContext context);
Task<PolicyAuthorizationResult> AuthorizeAsync(AuthorizationPolicy policy, AuthenticateResult authenticationResult, HttpContext context, object resource);
}

定义了两个方法AuthenticateAsyncAuthorizeAsync方法

IPolicyEvaluator的默认实现为PolicyEvaluator

    public class PolicyEvaluator : IPolicyEvaluator
{
private readonly IAuthorizationService _authorization; public PolicyEvaluator(IAuthorizationService authorization)
{
_authorization = authorization;
} public virtual async Task<AuthenticateResult> 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<PolicyAuthorizationResult> AuthorizeAsync(AuthorizationPolicy policy, AuthenticateResult authenticationResult, HttpContext context, object resource)
{
if (policy == null)
{
throw new ArgumentNullException(nameof(policy));
} 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();
}
}

授权中间件委托它来实现身份验证和授权处理,它内部会调用AuthorizationService,进而执行所有授权处理器AuthorizationHandler, (在后面会提到授权中间件用到这两个方法)

3.7.1、AuthenticateAsync

当授权策略没有设置AuthenticationSchemes,则只判断下当前请求是否已做身份验证,若做了就返回成功

当授权策略设置了AuthenticationSchemes,则遍历身份验证方案逐个进行身份验证处理 。

其中context.User就是使用context.AuthenticateAsync(DefaultAuthenticateScheme)来赋值的,将所有得到的用户标识重组成一个复合的用户标识。

当我们希望使用非默认的Scheme,或者是想合并多个认证Scheme的Claims时,就需要使用基于Scheme的授权来重置Claims了。

它的实现也很简单,直接使用我们在授权策略中指定的Schemes来依次调用认证服务的AuthenticateAsync方法,并将生成的Claims合并,最后返回我们熟悉的AuthenticateResult认证结果。

3.7.2、AuthorizeAsync

该方法会根据Requirements来完成授权,具体的实现是通过调用IAuthorizationService调用AuthorizeAsync来实现的。

最终返回的是一个PolicyAuthorizationResult对象,并在授权失败时,根据认证结果来返回Forbid(未授权)Challenge(未登录)


以上汇总

  1. 授权服务IAuthorizationService,接口的默认实现为DefaultAuthorizationService,进行授权验证。
  2. 在会根据授权策略提供器IAuthorizationPolicyProvider来获取指定名称的授权。
  3. 通过授权处理器上下文对象工厂IAuthorizationHandlerContextFactory授权处理器AuthorizationHandler在授权时需要传入AuthorizationHandlerContext(上面说了授权完成后的结果也存储在里面)。所以在执行授权处理器之前需要构建这个上下文对象,就是通过这个工厂构建的,主要的数据来源就是 当前 或者 指定的 授权策略AuthorizationPolicy。
  4. 所以这个时候会授权处理提供其 IAuthorizationHandlerProvider,来获取系统中所有授权处理器。
  5. 授权评估器IAuthorizationEvaluator来确定授权结果是否成功,在授权处理器AuthorizationHandler在执行完授权后,结果是存储在AuthorizationHandlerContext中的,这里的评估器只是根据AuthorizationHandlerContext创建一个授权结果AuthorizationResult。
  6. 上面所说的授权处理器就是IAuthorizationHandler,处理器中包含主要的授权逻辑,在处理的过程中会将所有的授权处理器一一验证。
  7. 所以在授权中间件中会利用IPolicyEvaluator中实现的身份认证和授权处理方法来调用AuthorizationService来执行所有的处理器。

四、中间件

在Configure中注册管道:运行使用调用方法来配置Http请求管道

    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<IAuthorizeData>() ?? Array.Empty<IAuthorizeData>();
var policy = await AuthorizationPolicy.CombineAsync(_policyProvider, authorizeData);
if (policy == null)
{
await _next(context);
return;
}
var policyEvaluator = context.RequestServices.GetRequiredService<IPolicyEvaluator>(); var authenticateResult = await policyEvaluator.AuthenticateAsync(policy, context);
// Allow Anonymous skips all authorization
if (endpoint?.Metadata.GetMetadata<IAllowAnonymous>() != null)
{
await _next(context);
return;
}
// Note that the resource will be null if there is no matched endpoint
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;
}
await _next(context);
}
}

进行代码分解:

  1. 拿到当前请求的的终结点
  var endpoint = context.GetEndpoint();
  1. 在当前请求拿到终结点endpoint的时候,会通过终结点拿到关联的IAuthorizeData集合
var authorizeData = endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() ?? Array.Empty<IAuthorizeData>();
  1. 将根据IAuthorizeData集合调用AuthorizationPolicy.CombineAsync()来创建组合策略(具体了可以看一下上一章) ( 用例: [Authorize(Policy = "BaseRole")]
var policy = await AuthorizationPolicy.CombineAsync(_policyProvider, authorizeData);
  1. IPolicyEvaluator获取策略评估器对得到的组合策略进行身份验证,多种身份验证得到的用户证件信息会合并进HttpContext.User
 var policyEvaluator = context.RequestServices.GetRequiredService<IPolicyEvaluator>();

  var authenticateResult = await policyEvaluator.AuthenticateAsync(policy, context);

  1. 当使用[AllowAnonymous]的时候,则直接跳过授权检验。
 if (endpoint?.Metadata.GetMetadata<IAllowAnonymous>() != null)
{
await _next(context);
return;
}
  1. IPolicyEvaluator提供的AuthorizeAsync授权检查方法,进行策略授权检查。
 var authorizeResult = await policyEvaluator.AuthorizeAsync(policy, authenticateResult, context, resource: endpoint);
  1. 当进行授权时,遍历策略所有的身份验证方案,进行质询,若策略里木有身份验证方案则使用默认身份验证方案进行质询。

当授权评估拒绝就直接调用身份验证方案进行拒绝。

 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来进行授权处理

五、总结

  1. 通过对上述的处理流程的分析,可以看出授权主要是通过IAuthorizationService来实现的,而我们进行使用只需要提供授权策略的Requirement,非常方便灵活的使用。
  2. 从源码权限设计来看,系统注册了各种服务,实现多种默认服务,加上默认的处理方式也满足了大部分应用需求, 所以可以看出这一块的功能还是很强大的,就算我们想通过自定义的方式来实现,也可以通过某些接口来实现拓展。
  3. 其中有很多核心源码怕说的不够清楚,所以在平时的开发项目中,再去看官方文档或源码这样理解应该更容易。
  4. 如果有不对的或不理解的地方,希望大家可以多多指正,提出问题,一起讨论,不断学习,共同进步。
  5. 参考的文档 和官方源码

基于.NetCore3.1系列 —— 认证授权方案之授权揭秘 (下篇)的更多相关文章

  1. 基于.NetCore3.1系列 ——认证授权方案之Swagger加锁

    一.前言 在之前的使用Swagger做Api文档中,我们已经使用Swagger进行开发接口文档,以及更加方便的使用.这一转换,让更多的接口可以以通俗易懂的方式展现给开发人员.而在后续的内容中,为了对a ...

  2. 基于.NetCore3.1系列 —— 日志记录之日志配置揭秘

    一.前言 在项目的开发维护阶段,有时候我们关注的问题不仅仅在于功能的实现,甚至需要关注系统发布上线后遇到的问题能否及时的查找并解决.所以我们需要有一个好的解决方案来及时的定位错误的根源并做出正确及时的 ...

  3. 基于.NetCore3.1系列 —— 日志记录之初识Serilog

    一.前言 对内置日志系统的整体实现进行了介绍之后,可以通过使用内置记录器来实现日志的输出路径.而在实际项目开发中,使用第三方日志框架(如: Log4Net.NLog.Loggr.Serilog.Sen ...

  4. 基于.NetCore3.1系列 —— 日志记录之自定义日志组件

    一.前言 回顾:日志记录之日志核心要素揭秘 在上一篇中,我们通过学习了解在.net core 中内置的日志记录中的几大核心要素,在日志工厂记录器(ILoggerFactory)中实现将日志记录提供器( ...

  5. 基于.NetCore3.1系列 —— 日志记录之日志核心要素揭秘

    一.前言 在上一篇中,我们已经了解了内置系统的默认配置和自定义配置的方式,在学习了配置的基础上,我们进一步的对日志在程序中是如何使用的深入了解学习.所以在这一篇中,主要是对日志记录的核心机制进行学习说 ...

  6. 认证授权方案之JwtBearer认证

    1.前言 回顾:认证方案之初步认识JWT 在现代Web应用程序中,即分为前端与后端两大部分.当前前后端的趋势日益剧增,前端设备(手机.平板.电脑.及其他设备)层出不穷.因此,为了方便满足前端设备与后端 ...

  7. OAuth2密码模式已死,最先进的Spring Cloud认证授权方案在这里

    旧的Spring Security OAuth2停止维护已经有一段时间了,99%的Spring Cloud微服务项目还在使用这些旧的体系,严重青黄不接.很多同学都在寻找新的解决方案,甚至还有念念不忘密 ...

  8. HTTP API认证授权方案

    目录 一.需求背景 二.常用的API认证技术 2.1 App Secret Key + HMAC 2.2 OAuth 2.0 2.2.1 Authorization Code Flow 2.2.2 C ...

  9. 基于HttpModule的简单.NET网站授权方案

    摘要 本文介绍一种入门级的网站授权(注:这里所指的授权指的是注册码效果,而不是网站登陆时的身份授权)方案,仅供学习交流及对付小白客户使用.复杂的网站授权涉及网站加密等一系列复杂的技术,不做本文介绍内容 ...

随机推荐

  1. PyQt5 FileDialog的使用例子

    加载***.ui文件可以使用: loadUi('main_window.ui', self) self.btnFileChoose.clicked.connect(self.getFolderName ...

  2. GitHub 热点速览 Vol.24:程序员自我增值,优雅赚零花钱

    摘要:升职加薪,出任 CTO,迎娶白富美/高帅富,走向人生巅峰是很多人的梦想.在本期的热点速览中你将了解自由作者 Easy 如何优雅赚取零花钱的方法,以及定投改变命运 -- 让时间陪你慢慢变富.说到程 ...

  3. python基础003----标准数据类型

    一.标准数据类型 在python中,只要定义了一个变量,而且它有数据,那么它的类型就已经确定了,不需要开发者主动去说明它的类型,系统会自动识别.可以用type(变量名)来查看变量的类型.常见的变量类型 ...

  4. Python按顺序读取文件夹中文件

    参考资料: https://blog.csdn.net/qq_22227123/article/details/79903116 https://blog.csdn.net/merdy_xi/arti ...

  5. Linux性能优化思路

    性能测试的核心,就是找出性能瓶颈并进行性能优化,解决"慢"的问题,最终满足客户业务需求. [性能需求来源及性能问题现象] 性能需求的来源,主要分为以下几类: 项目组提出性能需求: ...

  6. Spring如何解决循环依赖?

    介绍 先说一下什么是循环依赖,Spring在初始化A的时候需要注入B,而初始化B的时候需要注入A,在Spring启动后这2个Bean都要被初始化完成 Spring的循环依赖有两种场景 构造器的循环依赖 ...

  7. Springboot 集成 ElasticSearch 踩坑

    这里只涉及到基础使用 导包 <dependency> <groupId>org.springframework.boot</groupId> <artifac ...

  8. Ueditor富文本添加视频内容,视频不显示以及编辑富文本时,视频不显示解决方案

    问题是在添加视频时,编辑器会把视频标签<video>换成<img>.很讨厌... 1.2是解决添加视频时不显示,3是解决编辑时不显示 ueditor.all.js文件中  第7 ...

  9. Kubernetes 中 搭建 EFK 日志搜索中心

    简介 Elastic 官方已经发布了Elasticsearch Operator ,简化了 elasticsearch 以及 kibana的部署与升级,结合 fluentd-kubernetes-da ...

  10. RocketMQ入门到入土(二)事务消息&顺序消息

    接上一篇:RocketMQ入门到入土(一)新手也能看懂的原理和实战! 一.事务消息的由来 1.案例 引用官方的购物案例: 小明购买一个100元的东西,账户扣款100元的同时需要保证在下游的积分系统给小 ...