基于.NetCore3.1系列 —— 认证受权方案之受权揭秘 (下篇)

1、前言

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

在上一篇中,主要讲解了受权在配置方面的源码,从添加受权配置开始,咱们引入了须要的受权配置选项,而不一样的受权要求构建不一样的策略方式,从而实现一种本身满意的受权需求配置要求。git

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

2、说明

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

以前咱们进行对步骤一的受权有了大概了解,因此下面咱们将对步骤二进行的注册对象进行说明。app

3、开始

3.1 IAuthorizationService

受权服务接口,用来肯定受权是否成功的主要服务,接口的定义为asp.net

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

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

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

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.一、AuthenticateAsync

当受权策略没有设置AuthenticationSchemes,则只判断下当前请求是否已作身份验证,若作了就返回成功
当受权策略设置了AuthenticationSchemes,则遍历身份验证方案逐个进行身份验证处理 。

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

当咱们但愿使用非默认的Scheme,或者是想合并多个认证Scheme的Claims时,就须要使用基于Scheme的受权来重置Claims了。

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

3.7.二、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来执行全部的处理器。

4、中间件

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

5、总结

  1. 经过对上述的处理流程的分析,能够看出受权主要是经过IAuthorizationService来实现的,而咱们进行使用只须要提供受权策略的Requirement,很是方便灵活的使用。
  2. 从源码权限设计来看,系统注册了各类服务,实现多种默认服务,加上默认的处理方式也知足了大部分应用需求, 因此能够看出这一块的功能仍是很强大的,就算咱们想经过自定义的方式来实现,也能够经过某些接口来实现拓展。
  3. 其中有不少核心源码怕说的不够清楚,因此在平时的开发项目中,再去看官方文档或源码这样理解应该更容易。
  4. 若是有不对的或不理解的地方,但愿你们能够多多指正,提出问题,一块儿讨论,不断学习,共同进步。
  5. 参考的文档 和官方源码
相关文章
相关标签/搜索