日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > asp.net >内容正文

asp.net

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

發(fā)布時(shí)間:2023/12/4 asp.net 78 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于.NetCore3.1系列 —— 认证授权方案之授权揭秘 (下篇) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、前言

回顧:基于.NetCore3.1系列 —— 認(rèn)證授權(quán)方案之授權(quán)揭秘 (上篇)

在上一篇中,主要講解了授權(quán)在配置方面的源碼,從添加授權(quán)配置開(kāi)始,我們引入了需要的授權(quán)配置選項(xiàng),而不同的授權(quán)要求構(gòu)建不同的策略方式,從而實(shí)現(xiàn)一種自己滿意的授權(quán)需求配置要求。

在這一節(jié)中,繼續(xù)上一篇的內(nèi)容往下深入了解授權(quán)內(nèi)部機(jī)制的奧秘以及是如何實(shí)現(xiàn)執(zhí)行授權(quán)流程的。

二、說(shuō)明

在上一篇中,我們通過(guò)定義授權(quán)策略,查看源碼發(fā)現(xiàn),在對(duì)授權(quán)配置AuthorizationOptions之后,授權(quán)系統(tǒng)通過(guò)DI的方式注冊(cè)了幾個(gè)核心的默認(rèn)實(shí)現(xiàn)。

之前我們進(jìn)行對(duì)步驟一的授權(quán)有了大概了解,所以下面我們將對(duì)步驟二進(jìn)行的注冊(cè)對(duì)象進(jìn)行說(shuō)明。

三、開(kāi)始

3.1 IAuthorizationService

授權(quán)服務(wù)接口,用來(lái)確定授權(quán)是否成功的主要服務(wù),接口的定義為

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

兩個(gè)接口的參數(shù)不同之處在于IAuthorizationRequirement和policyName,分別是指定資源的一組特定要求和指定的授權(quán)名稱。

同時(shí)asp.net core還為IAuthorizationService接口拓展了幾個(gè)方法:

? ?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);}}

接口的默認(rèn)實(shí)現(xiàn)為DefaultAuthorizationService

DefaultAuthorizationService的實(shí)現(xiàn)主要是用來(lái)對(duì) IAuthorizationRequirement對(duì)象的授權(quán)檢驗(yàn)。

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);} }

通過(guò)上面的代碼可以發(fā)現(xiàn),在對(duì)象實(shí)例中,通過(guò)構(gòu)造函數(shù)的方式分別注入了IAuthorizationPolicyProvider、IAuthorizationHandlerProvider、IAuthorizationEvaluator、IAuthorizationHandlerContextFactory這幾個(gè)核心服務(wù),以及配置選項(xiàng)的AuthorizationOptions對(duì)象,再通過(guò)實(shí)現(xiàn)的方法AuthorizeAsync可以看出,在方法中調(diào)用GetPolicyAsync來(lái)獲取Requirements,具體的可以看一下上一節(jié)的AuthorizationPolicy,而后在根據(jù)授權(quán)上下文來(lái)判斷。

這里就用到了注入的幾個(gè)核心對(duì)象來(lái)實(shí)現(xiàn)完成授權(quán)的。下面會(huì)分別介紹到的。

3.2 IAuthorizationPolicyProvider

由上面的IAuthorizationServer接口的默認(rèn)實(shí)現(xiàn)可以發(fā)現(xiàn),在進(jìn)行授權(quán)檢驗(yàn)的時(shí)候,DefaultAuthorizationService會(huì)利用注入的IAuthorizationPolicyProvider服務(wù)來(lái)提供注冊(cè)的授權(quán)策略,所以我們查看源碼發(fā)現(xiàn),接口提供 了默認(rèn)的授權(quán)策略GetDefaultPolicyAsync和指定名稱的授權(quán)策略·GetPolicyAsync(string policyName)的方法。

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

再加上在使用[Authorize]進(jìn)行策略授權(quán)的時(shí)候,會(huì)根據(jù)提供的接口方法來(lái)獲取指定的授權(quán)策略。

IAuthorizationPolicyProvider來(lái)根據(jù)名稱獲取到策略對(duì)象,默認(rèn)實(shí)現(xiàn)為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));}}

由上面的代碼可以看出,在實(shí)現(xiàn)DefaultAuthorizationPolicyProvider對(duì)象進(jìn)行構(gòu)造函數(shù)的方式注入了IOptions<AuthorizationOptions> options服務(wù)來(lái)提供配置選項(xiàng)AuthorizationOptions(不懂的可以查看上一篇的AuthorizationOptions),再通過(guò)實(shí)現(xiàn)的方法可以看出是如何獲取到注冊(cè)的授權(quán)策略的了。附加一個(gè)圖片

在上一章中介紹過(guò),我們定義的策略都保存在AuthorizationOptions的中PolicyMap字典中,由上代碼可以發(fā)現(xiàn)這字典的用處。

3.3 IAuthorizationHandlerContextFactory

先看看這個(gè)接口的源代碼

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

接口定義了一個(gè)唯一的方法CreateContext,作用在于創(chuàng)建授權(quán)上下文AuthorizationHandlerContext對(duì)象。接口默認(rèn)實(shí)現(xiàn)方式

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

再來(lái)看看AuthorizationHandlerContext授權(quán)上下文對(duì)象,可以看出,上下文中主要包括用戶的Claims和授權(quán)策略的要求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);} }

因此,在下面我們剛好會(huì)提到了IAuthorizationHandlerProvider中的方法,可以根據(jù)授權(quán)上下文獲取到請(qǐng)求調(diào)用的處理程序。

3.4 IAuthorizationHandlerProvider ?

這個(gè)是接口的方法,作用是獲取所有的授權(quán)Handler

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

根據(jù)之前提到的授權(quán)上下文作為GetHandlersAsync方法參數(shù)對(duì)象來(lái)提取IAuthorizationHandler對(duì)象。

默認(rèn)接口的實(shí)現(xiàn)為DefaultAuthorizationHandlerProvider, 處理程序的默認(rèn)實(shí)現(xiàn),為授權(quán)請(qǐng)求提供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);}

從默認(rèn)實(shí)現(xiàn)的方式可以看出,利用構(gòu)造函數(shù)的方式注入默認(rèn)的IAuthorizationHandler的對(duì)象,但是我們?cè)倏纯唇涌诘膶?shí)現(xiàn)方法可以發(fā)現(xiàn),GetHandlersAsync返回的IAuthorizationHandler對(duì)象并不是從給定的AuthorizationHandlerContext上下文中獲取的,而是直接通過(guò)構(gòu)造函數(shù)的方式注入得到的。

這個(gè)時(shí)候,你可能會(huì)問(wèn),那么IAuthorizationHandler是在哪里注入的呢?

對(duì)應(yīng)下面的 IAuthorizationHandler

3.5 IAuthorizationEvaluator ?

由DefaultAuthorizationService中的授權(quán)方法過(guò)程調(diào)用了

? var result = _evaluator.Evaluate(authContext);

IAuthorizationEvaluator接口,來(lái)確定授權(quán)結(jié)果是否成功。

? ?public interface IAuthorizationEvaluator{AuthorizationResult Evaluate(AuthorizationHandlerContext context);}

IAuthorizationEvaluator的唯一方法Evaluate,該方法會(huì)根據(jù)之前提供的授權(quán)上下文返回一個(gè)表示授權(quán)成功的AuthorizationResult對(duì)象。默認(rèn)實(shí)現(xiàn)為DefaultAuthorizationEvaluator

public class DefaultAuthorizationEvaluator : IAuthorizationEvaluator {public AuthorizationResult Evaluate(AuthorizationHandlerContext context)=> context.HasSucceeded? AuthorizationResult.Success(): AuthorizationResult.Failed(context.HasFailed? AuthorizationFailure.ExplicitFail(): AuthorizationFailure.Failed(context.PendingRequirements)); }

由默認(rèn)實(shí)現(xiàn)可以看出,AuthorizationHandlerContext對(duì)象的HasSucceeded屬性決定了授權(quán)是否成功。當(dāng)驗(yàn)證通過(guò)時(shí),授權(quán)上下文中的HasSucceeded才會(huì)為T(mén)rue。

其中的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<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 }; }

這里的兩個(gè)授權(quán)結(jié)果 正是IAuthorizationService 進(jìn)行實(shí)現(xiàn)授權(quán)AuthorizeAsync來(lái)完成校驗(yàn)返回的結(jié)果。

3.6 IAuthorizationHandler

接口方式實(shí)現(xiàn),判斷是否授權(quán),實(shí)現(xiàn)此接口的類

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

如果允許授權(quán),可通過(guò)此接口的方法來(lái)決定是否允許授權(quán)。

之前我們還介紹到,我們定義的Requirement,可以直接實(shí)現(xiàn)IAuthorizationHandler接口,也可以單獨(dú)定義Handler,但是需要注冊(cè)到DI系統(tǒng)中去。

在默認(rèn)的AuthorizationHandlerProvider中,會(huì)從DI系統(tǒng)中獲取到我們注冊(cè)的所有Handler,最終調(diào)用其HandleAsync方法。

我們?cè)趯?shí)現(xiàn)IAuthorizationHandler接口時(shí),通常是繼承自AuthorizationHandler來(lái)實(shí)現(xiàn),它有如下定義:

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); }

如上,首先會(huì)在HandleAsync過(guò)濾出與Requirement對(duì)匹配的Handler,然后再調(diào)用其HandleRequirementAsync方法。

那我們定義的直接實(shí)現(xiàn)IAuthorizationHandler了接口的Requirement又是如何執(zhí)行的呢?

我們可以發(fā)現(xiàn),IAuthorizationHandler在AddAuthorization拓展方法中可以看到默認(rèn)注冊(cè)了一個(gè)PassThroughAuthorizationHandler默認(rèn)實(shí)現(xiàn)為:

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

它負(fù)責(zé)調(diào)用該策略中所有實(shí)現(xiàn)了IAuthorizationHandler接口的Requirement。通過(guò)接口實(shí)現(xiàn)的方法可以看出,當(dāng)PassThroughAuthorizationHandler對(duì)象的HandleAsync方法被執(zhí)行的時(shí)候,它會(huì)從AuthroizationHanderContext的Requirements屬性中提取所有的IAuthoizationHandler對(duì)象,并逐個(gè)調(diào)用它們的HandleAsync方法來(lái)實(shí)施授權(quán)檢驗(yàn)。

所以可以看到的出,PassThroughAuthorizationHandler是一個(gè)特殊并且重要的授權(quán)處理器類型,其特殊之處在于它并沒(méi)有實(shí)現(xiàn)針對(duì)某個(gè)具體規(guī)則的授權(quán)檢驗(yàn),但是AuthorizationHandlerContext上下文所有的IAuthorizationHandler都是通過(guò)該對(duì)象驅(qū)動(dòng)執(zhí)行的。

3.7 IPolicyEvaluator ?

接口的方式實(shí)現(xiàn),為特定需求類型調(diào)用的授權(quán)處理程序的基類

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

定義了兩個(gè)方法AuthenticateAsync和AuthorizeAsync方法

IPolicyEvaluator的默認(rèn)實(shí)現(xiàn)為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 challengereturn (authenticationResult.Succeeded)? PolicyAuthorizationResult.Forbid(): PolicyAuthorizationResult.Challenge();}}

授權(quán)中間件委托它來(lái)實(shí)現(xiàn)身份驗(yàn)證和授權(quán)處理,它內(nèi)部會(huì)調(diào)用AuthorizationService,進(jìn)而執(zhí)行所有授權(quán)處理器AuthorizationHandler, (在后面會(huì)提到授權(quán)中間件用到這兩個(gè)方法)

3.7.1、AuthenticateAsync

當(dāng)授權(quán)策略沒(méi)有設(shè)置AuthenticationSchemes,則只判斷下當(dāng)前請(qǐng)求是否已做身份驗(yàn)證,若做了就返回成功當(dāng)授權(quán)策略設(shè)置了AuthenticationSchemes,則遍歷身份驗(yàn)證方案逐個(gè)進(jìn)行身份驗(yàn)證處理 。

其中context.User就是使用context.AuthenticateAsync(DefaultAuthenticateScheme)來(lái)賦值的,將所有得到的用戶標(biāo)識(shí)重組成一個(gè)復(fù)合的用戶標(biāo)識(shí)。

當(dāng)我們希望使用非默認(rèn)的Scheme,或者是想合并多個(gè)認(rèn)證Scheme的Claims時(shí),就需要使用基于Scheme的授權(quán)來(lái)重置Claims了。

它的實(shí)現(xiàn)也很簡(jiǎn)單,直接使用我們?cè)谑跈?quán)策略中指定的Schemes來(lái)依次調(diào)用認(rèn)證服務(wù)的AuthenticateAsync方法,并將生成的Claims合并,最后返回我們熟悉的AuthenticateResult認(rèn)證結(jié)果。

3.7.2、AuthorizeAsync

該方法會(huì)根據(jù)Requirements來(lái)完成授權(quán),具體的實(shí)現(xiàn)是通過(guò)調(diào)用IAuthorizationService調(diào)用AuthorizeAsync來(lái)實(shí)現(xiàn)的。

最終返回的是一個(gè)PolicyAuthorizationResult對(duì)象,并在授權(quán)失敗時(shí),根據(jù)認(rèn)證結(jié)果來(lái)返回Forbid(未授權(quán))或Challenge(未登錄)。


以上匯總

  • 授權(quán)服務(wù)IAuthorizationService,接口的默認(rèn)實(shí)現(xiàn)為DefaultAuthorizationService,進(jìn)行授權(quán)驗(yàn)證。

  • 在會(huì)根據(jù)授權(quán)策略提供器IAuthorizationPolicyProvider來(lái)獲取指定名稱的授權(quán)。

  • 通過(guò)授權(quán)處理器上下文對(duì)象工廠IAuthorizationHandlerContextFactory授權(quán)處理器AuthorizationHandler在授權(quán)時(shí)需要傳入AuthorizationHandlerContext(上面說(shuō)了授權(quán)完成后的結(jié)果也存儲(chǔ)在里面)。所以在執(zhí)行授權(quán)處理器之前需要構(gòu)建這個(gè)上下文對(duì)象,就是通過(guò)這個(gè)工廠構(gòu)建的,主要的數(shù)據(jù)來(lái)源就是 當(dāng)前 或者 指定的 授權(quán)策略AuthorizationPolicy。

  • 所以這個(gè)時(shí)候會(huì)授權(quán)處理提供其 IAuthorizationHandlerProvider,來(lái)獲取系統(tǒng)中所有授權(quán)處理器。

  • 授權(quán)評(píng)估器IAuthorizationEvaluator來(lái)確定授權(quán)結(jié)果是否成功,在授權(quán)處理器AuthorizationHandler在執(zhí)行完授權(quán)后,結(jié)果是存儲(chǔ)在AuthorizationHandlerContext中的,這里的評(píng)估器只是根據(jù)AuthorizationHandlerContext創(chuàng)建一個(gè)授權(quán)結(jié)果AuthorizationResult。

  • 上面所說(shuō)的授權(quán)處理器就是IAuthorizationHandler,處理器中包含主要的授權(quán)邏輯,在處理的過(guò)程中會(huì)將所有的授權(quán)處理器一一驗(yàn)證。

  • 所以在授權(quán)中間件中會(huì)利用IPolicyEvaluator中實(shí)現(xiàn)的身份認(rèn)證和授權(quán)處理方法來(lái)調(diào)用AuthorizationService來(lái)執(zhí)行所有的處理器。

  • 四、中間件

    在Configure中注冊(cè)管道:運(yùn)行使用調(diào)用方法來(lái)配置Http請(qǐng)求管道

    ? ?public void Configure(IApplicationBuilder app, IWebHostEnvironment env){ ? ?app.UseRouting();//開(kāi)啟認(rèn)證授權(quán)app.UseAuthentication();app.UseAuthorization();}

    在這里使用了授權(quán)中間件來(lái)檢查授權(quán),來(lái)看看中間件的源碼AuthorizationMiddleware

    public class AuthorizationMiddleware {// Property key is used by Endpoint routing to determine if Authorization has runprivate 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 authorizationif (endpoint?.Metadata.GetMetadata<IAllowAnonymous>() != null){await _next(context);return;}// Note that the resource will be null if there is no matched endpointvar 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);} }

    進(jìn)行代碼分解:

  • 拿到當(dāng)前請(qǐng)求的的終結(jié)點(diǎn)

  • var endpoint = context.GetEndpoint();
  • 在當(dāng)前請(qǐng)求拿到終結(jié)點(diǎn)endpoint的時(shí)候,會(huì)通過(guò)終結(jié)點(diǎn)拿到關(guān)聯(lián)的IAuthorizeData集合

  • var authorizeData = endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() ?? Array.Empty<IAuthorizeData>();
  • 將根據(jù)IAuthorizeData集合調(diào)用AuthorizationPolicy.CombineAsync()來(lái)創(chuàng)建組合策略(具體了可以看一下上一章) ? ( 用例:[Authorize(Policy = "BaseRole")] ?)

  • var policy = await AuthorizationPolicy.CombineAsync(_policyProvider, authorizeData);
  • IPolicyEvaluator獲取策略評(píng)估器對(duì)得到的組合策略進(jìn)行身份驗(yàn)證,多種身份驗(yàn)證得到的用戶證件信息會(huì)合并進(jìn)HttpContext.User

  • var policyEvaluator = context.RequestServices.GetRequiredService<IPolicyEvaluator>();var authenticateResult = await policyEvaluator.AuthenticateAsync(policy, context);
  • 當(dāng)使用[AllowAnonymous]的時(shí)候,則直接跳過(guò)授權(quán)檢驗(yàn)。?

  • if (endpoint?.Metadata.GetMetadata<IAllowAnonymous>() != null){await _next(context);return;}
  • 將IPolicyEvaluator提供的AuthorizeAsync授權(quán)檢查方法,進(jìn)行策略授權(quán)檢查。

  • var authorizeResult = await policyEvaluator.AuthorizeAsync(policy, authenticateResult, context, resource: endpoint);
  • 當(dāng)進(jìn)行授權(quán)時(shí),遍歷策略所有的身份驗(yàn)證方案,進(jìn)行質(zhì)詢,若策略里木有身份驗(yàn)證方案則使用默認(rèn)身份驗(yàn)證方案進(jìn)行質(zhì)詢。

  • 當(dāng)授權(quán)評(píng)估拒絕就直接調(diào)用身份驗(yàn)證方案進(jìn)行拒絕。

    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; }

    整個(gè)過(guò)程中,授權(quán)中間件會(huì)調(diào)用授權(quán)服務(wù)IAuthorizationService來(lái)進(jìn)行授權(quán)處理

    五、總結(jié)

  • 通過(guò)對(duì)上述的處理流程的分析,可以看出授權(quán)主要是通過(guò)IAuthorizationService來(lái)實(shí)現(xiàn)的,而我們進(jìn)行使用只需要提供授權(quán)策略的Requirement,非常方便靈活的使用。

  • 從源碼權(quán)限設(shè)計(jì)來(lái)看,系統(tǒng)注冊(cè)了各種服務(wù),實(shí)現(xiàn)多種默認(rèn)服務(wù),加上默認(rèn)的處理方式也滿足了大部分應(yīng)用需求, 所以可以看出這一塊的功能還是很強(qiáng)大的,就算我們想通過(guò)自定義的方式來(lái)實(shí)現(xiàn),也可以通過(guò)某些接口來(lái)實(shí)現(xiàn)拓展。

  • 其中有很多核心源碼怕說(shuō)的不夠清楚,所以在平時(shí)的開(kāi)發(fā)項(xiàng)目中,再去看官方文檔或源碼這樣理解應(yīng)該更容易。

  • 如果有不對(duì)的或不理解的地方,希望大家可以多多指正,提出問(wèn)題,一起討論,不斷學(xué)習(xí),共同進(jìn)步。

  • 參考的文檔 和官方源碼

  • 往期精彩回顧

    總結(jié)

    以上是生活随笔為你收集整理的基于.NetCore3.1系列 —— 认证授权方案之授权揭秘 (下篇)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。