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

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

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

认证授权方案之授权揭秘 (上篇)

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

一、前言

回顧:認(rèn)證授權(quán)方案之授權(quán)初識(shí)

從上一節(jié)中,我們?cè)趯?duì)授權(quán)系統(tǒng)已經(jīng)有了初步的認(rèn)識(shí)和使用,可以發(fā)現(xiàn),asp.net core為我們提供的授權(quán)策略是一個(gè)非常強(qiáng)大豐富且靈活的認(rèn)證授權(quán)方案,能夠滿(mǎn)足大部分的授權(quán)場(chǎng)景。

在ConfigureServices中配置服務(wù):將授權(quán)服務(wù)添加到容器

? ?public void ConfigureServices(IServiceCollection services){services.AddAuthorization(options =>{options.AddPolicy("customizePermisson",policy => policy.Requirements.Add(new PermissionRequirement("user")));});//此外,還需要在 IAuthorizationHandler 類(lèi)型的范圍內(nèi)向 DI 系統(tǒng)注冊(cè)新的處理程序:services.AddScoped<IAuthorizationHandler, PermissionRequirementHandler>();}

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

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

通過(guò)以上幾行代碼的實(shí)現(xiàn),就可以進(jìn)行授權(quán)了,這個(gè)時(shí)候,你可以會(huì)問(wèn),這幾行代碼都進(jìn)行了什么操作實(shí)現(xiàn)授權(quán)的?

好了,繼續(xù)回到上節(jié)最后說(shuō)的在這一節(jié)中對(duì)授權(quán)策略的核心進(jìn)行一步步的揭秘的。

二、開(kāi)始

引入整體結(jié)構(gòu)

2.1 ?添加授權(quán)AddAuthorization

添加授權(quán)策略服務(wù)使用AddAuthorization方法,以便調(diào)用。

從源碼可以發(fā)現(xiàn),從core3.0后,由之前在core2.0中的AuthorizationServiceCollectionExtensions.cs文件中,原來(lái)的AddAuthorization的方法變?yōu)榱薃ddAuthorizationCore方法,微軟在這一塊進(jìn)行了封裝在PolicyServiceCollectionExtensions.cs文件中,沿用了之前AddAuthorization拓展名稱(chēng),不影響之前版本的使用。

我們來(lái)看看aspnetcore源碼:

? ?public static class PolicyServiceCollectionExtensions{public static IServiceCollection AddAuthorizationPolicyEvaluator(this IServiceCollection services){if (services == null){throw new ArgumentNullException(nameof(services));}services.TryAddSingleton<AuthorizationPolicyMarkerService>();services.TryAddTransient<IPolicyEvaluator, PolicyEvaluator>();services.TryAddTransient<IAuthorizationMiddlewareResultHandler, AuthorizationMiddlewareResultHandler>();return services;}public static IServiceCollection AddAuthorization(this IServiceCollection services){if (services == null){throw new ArgumentNullException(nameof(services));}services.AddAuthorizationCore();services.AddAuthorizationPolicyEvaluator();return services;} ? ? ?public static IServiceCollection AddAuthorization(this IServiceCollection services, Action<AuthorizationOptions> configure){if (services == null){throw new ArgumentNullException(nameof(services));}services.AddAuthorizationCore(configure);services.AddAuthorizationPolicyEvaluator();return services;}} ? ?public static class AuthorizationServiceCollectionExtensions{public static IServiceCollection AddAuthorizationCore(this IServiceCollection services){if (services == null){throw new ArgumentNullException(nameof(services));}services.TryAdd(ServiceDescriptor.Transient<IAuthorizationService, DefaultAuthorizationService>());services.TryAdd(ServiceDescriptor.Transient<IAuthorizationPolicyProvider, DefaultAuthorizationPolicyProvider>());services.TryAdd(ServiceDescriptor.Transient<IAuthorizationHandlerProvider, DefaultAuthorizationHandlerProvider>());services.TryAdd(ServiceDescriptor.Transient<IAuthorizationEvaluator, DefaultAuthorizationEvaluator>()); ? ? ? ? ? ? ? services.TryAdd(ServiceDescriptor.Transient<IAuthorizationHandlerContextFactory, DefaultAuthorizationHandlerContextFactory>());services.TryAddEnumerable(ServiceDescriptor.Transient<IAuthorizationHandler, PassThroughAuthorizationHandler>());return services;}public static IServiceCollection AddAuthorizationCore(this IServiceCollection services, Action<AuthorizationOptions> configure){if (services == null){throw new ArgumentNullException(nameof(services));}services.Configure(configure);return services.AddAuthorizationCore();}}

由上可知,在調(diào)用AddAuthorization方法進(jìn)行授權(quán)配置的時(shí)候,需要使用到AuthorizationOptions委托方式傳參。

所以我們?cè)賮?lái)看看下面這一行代碼,通過(guò)AddPolicy實(shí)現(xiàn)添加策略方式。

? options.AddPolicy("customizePermisson",policy => policy.Requirements.Add(new PermissionRequirement("user")));

查看源碼發(fā)現(xiàn)是引用了AuthorizationOptions對(duì)象。

2.2 ? 配置選項(xiàng)AuthorizationOptions

授權(quán)選項(xiàng)實(shí)現(xiàn)添加和授權(quán)配置,提供授權(quán)服務(wù)的配置。

源碼如下:

? ?public class AuthorizationOptions{private Dictionary<string, AuthorizationPolicy> PolicyMap { get; } = new Dictionary<string, AuthorizationPolicy>(StringComparer.OrdinalIgnoreCase);public bool InvokeHandlersAfterFailure { get; set; } = true;public AuthorizationPolicy DefaultPolicy { get; set; } = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();public AuthorizationPolicy? FallbackPolicy { get; set; }public void AddPolicy(string name, AuthorizationPolicy policy){if (name == null){throw new ArgumentNullException(nameof(name));}if (policy == null){throw new ArgumentNullException(nameof(policy));}PolicyMap[name] = policy;}public void AddPolicy(string name, Action<AuthorizationPolicyBuilder> configurePolicy){if (name == null){throw new ArgumentNullException(nameof(name));}if (configurePolicy == null){throw new ArgumentNullException(nameof(configurePolicy));}var policyBuilder = new AuthorizationPolicyBuilder();configurePolicy(policyBuilder);PolicyMap[name] = policyBuilder.Build();}public AuthorizationPolicy GetPolicy(string name){if (name == null){throw new ArgumentNullException(nameof(name));}if (PolicyMap.TryGetValue(name, out var value)){return value;}return null;}}

定義一個(gè)字典

private Dictionary<string, AuthorizationPolicy> PolicyMap { get; } = new Dictionary<string, AuthorizationPolicy>(StringComparer.OrdinalIgnoreCase);

目的在于將定義的授權(quán)策略方式都保存在這個(gè)聲明的PolicyMap當(dāng)中,而其中AddPolicy方法是將配置的策略添加到字典中。

public void AddPolicy(string name, AuthorizationPolicy policy);public void AddPolicy(string name, Action<AuthorizationPolicyBuilder> configurePolicy);

而這方法中涉及到兩種不同的傳參對(duì)象AuthorizationPolicy和AuthorizationPolicyBuilder。

2.3 ?授權(quán)策略 AuthorizationPolicy

表示授權(quán)要求和方案的集合。具體源碼如下:

public class AuthorizationPolicy {public AuthorizationPolicy(IEnumerable<IAuthorizationRequirement> requirements, IEnumerable<string> authenticationSchemes){if (requirements == null){throw new ArgumentNullException(nameof(requirements));}if (authenticationSchemes == null){throw new ArgumentNullException(nameof(authenticationSchemes));}if (requirements.Count() == 0){throw new InvalidOperationException(Resources.Exception_AuthorizationPolicyEmpty);}Requirements = new List<IAuthorizationRequirement>(requirements).AsReadOnly();AuthenticationSchemes = new List<string>(authenticationSchemes).AsReadOnly();}public IReadOnlyList<IAuthorizationRequirement> Requirements { get; }public IReadOnlyList<string> AuthenticationSchemes { get; }public static AuthorizationPolicy Combine(params AuthorizationPolicy[] policies){if (policies == null){throw new ArgumentNullException(nameof(policies));}return Combine((IEnumerable<AuthorizationPolicy>)policies);}public static AuthorizationPolicy Combine(IEnumerable<AuthorizationPolicy> policies){if (policies == null){throw new ArgumentNullException(nameof(policies));}var builder = new AuthorizationPolicyBuilder();foreach (var policy in policies){builder.Combine(policy);}return builder.Build();}public static async Task<AuthorizationPolicy> CombineAsync(IAuthorizationPolicyProvider policyProvider, IEnumerable<IAuthorizeData> authorizeData){if (policyProvider == null){throw new ArgumentNullException(nameof(policyProvider));}if (authorizeData == null){throw new ArgumentNullException(nameof(authorizeData));}// Avoid allocating enumerator if the data is known to be emptyvar skipEnumeratingData = false;if (authorizeData is IList<IAuthorizeData> dataList){skipEnumeratingData = dataList.Count == 0;}AuthorizationPolicyBuilder policyBuilder = null;if (!skipEnumeratingData){foreach (var authorizeDatum in authorizeData){if (policyBuilder == null){policyBuilder = new AuthorizationPolicyBuilder();}var useDefaultPolicy = true;if (!string.IsNullOrWhiteSpace(authorizeDatum.Policy)){var policy = await policyProvider.GetPolicyAsync(authorizeDatum.Policy);if (policy == null){throw new InvalidOperationException(Resources.FormatException_AuthorizationPolicyNotFound(authorizeDatum.Policy));}policyBuilder.Combine(policy);useDefaultPolicy = false;}var rolesSplit = authorizeDatum.Roles?.Split(',');if (rolesSplit != null && rolesSplit.Any()){var trimmedRolesSplit = rolesSplit.Where(r => !string.IsNullOrWhiteSpace(r)).Select(r => r.Trim());policyBuilder.RequireRole(trimmedRolesSplit);useDefaultPolicy = false;}var authTypesSplit = authorizeDatum.AuthenticationSchemes?.Split(',');if (authTypesSplit != null && authTypesSplit.Any()){foreach (var authType in authTypesSplit){if (!string.IsNullOrWhiteSpace(authType)){policyBuilder.AuthenticationSchemes.Add(authType.Trim());}}}if (useDefaultPolicy){policyBuilder.Combine(await policyProvider.GetDefaultPolicyAsync());}}}// If we have no policy by now, use the fallback policy if we have oneif (policyBuilder == null){var fallbackPolicy = await policyProvider.GetFallbackPolicyAsync();if (fallbackPolicy != null){return fallbackPolicy;}}return policyBuilder?.Build();} }

我們從源碼中可以發(fā)現(xiàn),Authorization 對(duì)象 Combine方法目的在于將授權(quán)策略進(jìn)行合并,同時(shí)調(diào)用了AuthorizationPolicyBuilder對(duì)象中Combine方法,對(duì)授權(quán)方案或者授權(quán)策略進(jìn)行合并。再來(lái)看看AuthorizationPolicy對(duì)象中的CombineAsync方法,這里的參數(shù)用到了IAuthorizeData,同時(shí)這個(gè)方法的過(guò)程是將可能基于角色,基于方案或者基于策略都合并轉(zhuǎn)換為是授權(quán)策略的方式,也是通過(guò)調(diào)用AuthorizationPolicyBuilder對(duì)象來(lái)實(shí)現(xiàn)合并。所以可以看得出AuthorizationPolicyBuilder提供了一些創(chuàng)建AuthorizationPolicy的方法。

這個(gè)時(shí)候,我們可以發(fā)現(xiàn),其實(shí)之前說(shuō)的基于角色、基于方案的授權(quán)方式本質(zhì)上來(lái)說(shuō)都是基于策略授權(quán)。

2.4 ?構(gòu)建策略AuthorizationPolicyBuilder

除了上面說(shuō)到使用AuthorizationPolicy對(duì)象之外,我們還可以用AuthorizationPolicyBuilder對(duì)象以Buider來(lái)創(chuàng)建AuthorizationPolicy對(duì)象,將多個(gè)AuthorizationPolicy對(duì)象提供的數(shù)組進(jìn)行合并,所以AuthorizationPolicyBuilder提供的Combine方法的使用,為AuthorizationPolicy授權(quán)構(gòu)建提供了許多便捷的方式。

public class AuthorizationPolicyBuilder {public AuthorizationPolicyBuilder(params string[] authenticationSchemes){AddAuthenticationSchemes(authenticationSchemes);}public AuthorizationPolicyBuilder(AuthorizationPolicy policy){Combine(policy);}public IList<IAuthorizationRequirement> Requirements { get; set; } = new List<IAuthorizationRequirement>(); ? ?public IList<string> AuthenticationSchemes { get; set; } = new List<string>();public AuthorizationPolicyBuilder AddAuthenticationSchemes(params string[] schemes){foreach (var authType in schemes){AuthenticationSchemes.Add(authType);}return this;} ? ?public AuthorizationPolicyBuilder AddRequirements(params IAuthorizationRequirement[] requirements){foreach (var req in requirements){Requirements.Add(req);}return this;}public AuthorizationPolicyBuilder Combine(AuthorizationPolicy policy){if (policy == null){throw new ArgumentNullException(nameof(policy));}AddAuthenticationSchemes(policy.AuthenticationSchemes.ToArray());AddRequirements(policy.Requirements.ToArray());return this;} ? ?public AuthorizationPolicyBuilder RequireClaim(string claimType, params string[] allowedValues){if (claimType == null){throw new ArgumentNullException(nameof(claimType));}return RequireClaim(claimType, (IEnumerable<string>)allowedValues);}public AuthorizationPolicyBuilder RequireClaim(string claimType, IEnumerable<string> allowedValues){if (claimType == null){throw new ArgumentNullException(nameof(claimType));}Requirements.Add(new ClaimsAuthorizationRequirement(claimType, allowedValues));return this;} ? ?public AuthorizationPolicyBuilder RequireClaim(string claimType){if (claimType == null){throw new ArgumentNullException(nameof(claimType));}Requirements.Add(new ClaimsAuthorizationRequirement(claimType, allowedValues: null));return this;} ? ?public AuthorizationPolicyBuilder RequireRole(params string[] roles){if (roles == null){throw new ArgumentNullException(nameof(roles));}return RequireRole((IEnumerable<string>)roles);} ?public AuthorizationPolicyBuilder RequireRole(IEnumerable<string> roles){if (roles == null){throw new ArgumentNullException(nameof(roles));}Requirements.Add(new RolesAuthorizationRequirement(roles));return this;} ? ?public AuthorizationPolicyBuilder RequireUserName(string userName){if (userName == null){throw new ArgumentNullException(nameof(userName));}Requirements.Add(new NameAuthorizationRequirement(userName));return this;}public AuthorizationPolicyBuilder RequireAuthenticatedUser(){Requirements.Add(new DenyAnonymousAuthorizationRequirement());return this;}public AuthorizationPolicyBuilder RequireAssertion(Func<AuthorizationHandlerContext, bool> handler){if (handler == null){throw new ArgumentNullException(nameof(handler));}Requirements.Add(new AssertionRequirement(handler));return this;}public AuthorizationPolicyBuilder RequireAssertion(Func<AuthorizationHandlerContext, Task<bool>> handler){if (handler == null){throw new ArgumentNullException(nameof(handler));}Requirements.Add(new AssertionRequirement(handler));return this;}public AuthorizationPolicy Build(){return new AuthorizationPolicy(Requirements, AuthenticationSchemes.Distinct());} }

由上面多出出現(xiàn)的IAuthorizationRequirement對(duì)象可以發(fā)現(xiàn),授權(quán)要求Requirement屬性是策略的核心方案,每一種Requirement都代表一種授權(quán)方式。同時(shí)IAuthorizationPolicyBuilder為這些預(yù)定義的方案創(chuàng)建了它們對(duì)應(yīng)的使用方式并將其添加到Requirements集合中。

2.5 授權(quán)要求IAuthorizationRequirement

? ?public interface IAuthorizationRequirement{}

接口并沒(méi)有任何實(shí)現(xiàn)成員,因?yàn)槭跈?quán)要求是具有不同的表現(xiàn)形式的,所有才沒(méi)有具體的實(shí)現(xiàn)成員。授權(quán)要求目的在于檢驗(yàn)?zāi)硞€(gè)當(dāng)前用戶(hù)是否具有相應(yīng)的要求, ?所以大部分IAuthorizationRequirement接口的實(shí)現(xiàn)類(lèi)都繼承了IAuthorizationHandler 接口來(lái)提供HandleAsync方法來(lái)實(shí)現(xiàn)對(duì)應(yīng)的授權(quán)檢驗(yàn)。

下面介紹asp.net core框架里面默認(rèn)實(shí)現(xiàn)的幾種IAuthorizationRequirement實(shí)現(xiàn)類(lèi)型。

2.5.1 DenyAnonymousAuthorizationRequirement

阻止匿名用戶(hù)操作,言外之意就是拒絕未被驗(yàn)證的匿名用戶(hù)訪問(wèn)資源。

源碼如下:

public class DenyAnonymousAuthorizationRequirement : AuthorizationHandler<DenyAnonymousAuthorizationRequirement>, IAuthorizationRequirement {protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, DenyAnonymousAuthorizationRequirement requirement){var user = context.User;var userIsAnonymous =user?.Identity == null ||!user.Identities.Any(i => i.IsAuthenticated);if (!userIsAnonymous){context.Succeed(requirement);}return Task.CompletedTask;} }

通過(guò)用戶(hù)的CliamPrincipal對(duì)象身份是否為空或是否是一個(gè)經(jīng)過(guò)認(rèn)證的用戶(hù)身份,以此來(lái)確定當(dāng)前請(qǐng)求的用戶(hù)是否來(lái)源于匿名用戶(hù)。

2.5.2 NameAuthorizationRequirement

指定用戶(hù)名的授權(quán)方式,判斷當(dāng)前用戶(hù)與某個(gè)指定的用戶(hù)是否匹配以此來(lái)授權(quán)訪問(wèn)資源。

源碼如下:

public class NameAuthorizationRequirement : AuthorizationHandler<NameAuthorizationRequirement>, IAuthorizationRequirement {public NameAuthorizationRequirement(string requiredName){if (requiredName == null){throw new ArgumentNullException(nameof(requiredName));}RequiredName = requiredName;}public string RequiredName { get; }protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, NameAuthorizationRequirement requirement){if (context.User != null){if (context.User.Identities.Any(i => string.Equals(i.Name, requirement.RequiredName))){context.Succeed(requirement);}}return Task.CompletedTask;} }

其中RequiredName屬性為授權(quán)用戶(hù),通過(guò)HandleRequirementAsync方法進(jìn)行校驗(yàn)當(dāng)前用戶(hù)的ClaimPrincipal對(duì)象的身份與RequiredName是否具有匹配。

這里的判斷用的是 string.Equals() 說(shuō)明這里比較的用戶(hù)名是區(qū)別大小寫(xiě)的。

2.5.3 ClaimsAuthorizationRequirement

基于指定聲明類(lèi)型的授權(quán)策略,檢驗(yàn)當(dāng)前用戶(hù)是否聲明類(lèi)型和候選值。

源碼如下:

public class ClaimsAuthorizationRequirement : AuthorizationHandler<ClaimsAuthorizationRequirement>, IAuthorizationRequirement{public ClaimsAuthorizationRequirement(string claimType, IEnumerable<string> allowedValues){if (claimType == null){throw new ArgumentNullException(nameof(claimType));}ClaimType = claimType;AllowedValues = allowedValues;}public string ClaimType { get; }public IEnumerable<string> AllowedValues { get; }protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ClaimsAuthorizationRequirement requirement){if (context.User != null){var found = false;if (requirement.AllowedValues == null || !requirement.AllowedValues.Any()){found = context.User.Claims.Any(c => string.Equals(c.Type, requirement.ClaimType, StringComparison.OrdinalIgnoreCase));}else{found = context.User.Claims.Any(c => string.Equals(c.Type, requirement.ClaimType, StringComparison.OrdinalIgnoreCase)&& requirement.AllowedValues.Contains(c.Value, StringComparer.Ordinal));}if (found){context.Succeed(requirement);}}return Task.CompletedTask;}}

由上我們可以看的出,ClaimType和AllowedValues這兩個(gè)屬性在構(gòu)造函數(shù)中被初始化,分別用來(lái)表示當(dāng)前聲明的聲明類(lèi)型和默認(rèn)允許值。通過(guò)HandleRequirementAsync來(lái)授權(quán)檢驗(yàn)是否完成通過(guò)。

2.5.4 ?RolesAuthorizationRequirement

基于角色的授權(quán)策略,檢驗(yàn)當(dāng)前用戶(hù)是否擁有約定匹配的角色,如果擁有,則可以訪問(wèn)對(duì)應(yīng)的資源。

源碼如下:

public class RolesAuthorizationRequirement : AuthorizationHandler<RolesAuthorizationRequirement>, IAuthorizationRequirement {public RolesAuthorizationRequirement(IEnumerable<string> allowedRoles){if (allowedRoles == null){throw new ArgumentNullException(nameof(allowedRoles));}if (allowedRoles.Count() == 0){throw new InvalidOperationException(Resources.Exception_RoleRequirementEmpty);}AllowedRoles = allowedRoles;}public IEnumerable<string> AllowedRoles { get; }protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RolesAuthorizationRequirement requirement){if (context.User != null){bool found = false;if (requirement.AllowedRoles == null || !requirement.AllowedRoles.Any()){// Review: What do we want to do here? No roles requested is auto success?}else{found = requirement.AllowedRoles.Any(r => context.User.IsInRole(r));}if (found){context.Succeed(requirement);}}return Task.CompletedTask;} }

其中AllowedRoles表示目標(biāo)角色列表的集合。通過(guò)HandleRequirementAsync實(shí)現(xiàn)授權(quán)檢驗(yàn),調(diào)用IsInRole方法來(lái)判斷當(dāng)前用戶(hù)的ClaimsPrincipal對(duì)象是否有指定的角色。

2.5.5 ?AssertionRequirement

基于AuthorizationHandlerContext上下文斷言的形式來(lái)聲明授權(quán)。

源碼如下:

public class AssertionRequirement : IAuthorizationHandler, IAuthorizationRequirement {public Func<AuthorizationHandlerContext, Task<bool>> Handler { get; }public AssertionRequirement(Func<AuthorizationHandlerContext, bool> handler){if (handler == null){throw new ArgumentNullException(nameof(handler));}Handler = context => Task.FromResult(handler(context));}public AssertionRequirement(Func<AuthorizationHandlerContext, Task<bool>> handler){if (handler == null){throw new ArgumentNullException(nameof(handler));}Handler = handler;}public async Task HandleAsync(AuthorizationHandlerContext context){if (await Handler(context)){context.Succeed(this);}} }

通過(guò)類(lèi)型為Func<AuthorizationHandlerContext, Task<bool>>的委托來(lái)表示該斷言,利用它來(lái)授權(quán)驗(yàn)證。在HandleAsync檢驗(yàn)方法中,直接調(diào)用這個(gè)委托對(duì)象來(lái)完成判斷。

2.5.6 OperationAuthorizationRequirement

基于預(yù)定義操作的授權(quán)策略。

源碼如下:

public class OperationAuthorizationRequirement : IAuthorizationRequirement {public string Name { get; set; } }

由上可知,只是包含一個(gè)操作名字的Name屬性,目的在于將授權(quán)的目標(biāo)對(duì)象映射到一個(gè)預(yù)定義的操作上。

三、用例

出現(xiàn)的IAuthorizationRequirement對(duì)象可以發(fā)現(xiàn),授權(quán)要求Requirement屬性是策略的核心方案,每一中Requirement都代表一種授權(quán)方式。

在上文我們通過(guò)構(gòu)建策略AuthorizationPolicyBuilder對(duì)象的源碼可以發(fā)現(xiàn),為我們提供了多個(gè)方法由預(yù)定義的IAuthorizationRequirement類(lèi)型來(lái)創(chuàng)建并將其添加到Requirements集合中。

3.1 應(yīng)用

實(shí)例應(yīng)用如下:在ConfigureServices中配置服務(wù)中

? ? ? ?public void ConfigureServices(IServiceCollection services){services.AddControllers();var combindPolicy = new AuthorizationPolicyBuilder().RequireClaim("role").Build();services.AddAuthorization(options =>{//DenyAnonymousAuthorizationRequirementoptions.AddPolicy("DenyAnonyUser", policy => policy.RequireAuthenticatedUser());//NameAuthorizationRequirementoptions.AddPolicy("NameAuth", policy => policy.RequireUserName("艾三元"));//ClaimsAuthorizationRequirementoptions.AddPolicy("ClaimsAuth", policy => policy.RequireClaim("role","admin"));//RolesAuthorizationRequirementoptions.AddPolicy("RolesAuth", policy => policy.RequireRole("admin","user"));//AssertionRequirementoptions.AddPolicy("AssertAuth", policy => policy.RequireAssertion(c=>c.User.HasClaim(o=>o.Type=="role")));//同樣可可用直接調(diào)用Combind方法,策略AuthorizationPolicyoptions.AddPolicy("CombindAuth", policy => policy.Combine(combindPolicy));}); }

以上,分別實(shí)現(xiàn)了框架中默認(rèn)實(shí)現(xiàn)的幾種IAuthorizationRequirement實(shí)現(xiàn)類(lèi)型在實(shí)際中的應(yīng)用,通過(guò)不同授權(quán)要求實(shí)現(xiàn)的策略方式,同時(shí)也可以將上面多種方式合并成一個(gè)對(duì)象,進(jìn)行調(diào)用使用。

3.2 拓展

當(dāng)然了,除了自帶了這幾種默認(rèn)實(shí)現(xiàn)方式之外,我們也可以通過(guò)自定義Requirement來(lái)滿(mǎn)足我們的需求。

這個(gè)在上一節(jié)初識(shí)授權(quán)的時(shí)候,已經(jīng)提到了自定義授權(quán)這一塊,所以在這里再看一次。

定義一個(gè)權(quán)限策略PermissionRequirement,這個(gè)策略并包含一些屬性。

public class PermissionRequirement: IAuthorizationRequirement {public string _permissionName { get; }public PermissionRequirement(string PermissionName){_permissionName = PermissionName;} }

再定義一個(gè)策略處理類(lèi)

public class PermissionRequirementHandler : AuthorizationHandler<PermissionRequirement> {protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement){var role = context.User.FindFirst(c => c.Type == ClaimTypes.Role);if (role != null){var roleValue = role.Value;if (roleValue==requirement._permissionName){context.Succeed(requirement);}}return Task.CompletedTask;

配置使用

? ?public void ConfigureServices(IServiceCollection services){services.AddControllers();//基于自定義策略授權(quán)services.AddAuthorization(options =>{options.AddPolicy("customizePermisson",policy => policy.Requirements.Add(new PermissionRequirement("admin")));});//此外,還需要在 IAuthorizationHandler 類(lèi)型的范圍內(nèi)向 DI 系統(tǒng)注冊(cè)新的處理程序:services.AddScoped<IAuthorizationHandler, PermissionRequirementHandler>();// 如前所述,要求可包含多個(gè)處理程序。如果為授權(quán)層的同一要求向 DI 系統(tǒng)注冊(cè)多個(gè)處理程序,有一個(gè)成功就足夠了。}

特別說(shuō)明

上述使用的處理程序是一對(duì)一的關(guān)系,當(dāng)聲明要求滿(mǎn)足條件的時(shí)候,則任務(wù)授權(quán)成功, 授權(quán)成功后, context.Succeed 將通過(guò)滿(mǎn)足要求作為其唯一參數(shù)調(diào)用。

但是授權(quán)策略中也包含一對(duì)多的要求關(guān)系,它們屬于 & 的關(guān)系,只用全部驗(yàn)證通過(guò),才能最終授權(quán)成功。但是在有些場(chǎng)景下,我們可能希望一個(gè)授權(quán)策略可以適用多種情況,比如,我們進(jìn)入公司時(shí)需要出示員工卡才可以被授權(quán)進(jìn)入,但是如果我們忘了帶員工卡,可以去申請(qǐng)一個(gè)臨時(shí)卡,同樣可以授權(quán)成功。

這里貼一個(gè)官方文檔的寫(xiě)法:public class BuildingEntryRequirement : IAuthorizationRequirement { } public class BadgeEntryHandler : AuthorizationHandler<BuildingEntryRequirement> {protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,BuildingEntryRequirement requirement){if (context.User.HasClaim(c => c.Type == "BadgeId" &&c.Issuer == "http://microsoftsecurity")){context.Succeed(requirement);}//TODO: Use the following if targeting a version of//.NET Framework older than 4.6:// ? ? return Task.FromResult(0);return Task.CompletedTask;} } public class TemporaryStickerHandler : AuthorizationHandler<BuildingEntryRequirement> {protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,BuildingEntryRequirement requirement){if (context.User.HasClaim(c => c.Type == "TemporaryBadgeId" &&c.Issuer == "https://microsoftsecurity")){// We'd also check the expiration date on the sticker.context.Succeed(requirement);}//TODO: Use the following if targeting a version of//.NET Framework older than 4.6:// ? ? return Task.FromResult(0);return Task.CompletedTask;} }

我們定義了兩個(gè)Handler,但是想讓它們得到執(zhí)行,還需要將其注冊(cè)到DI系統(tǒng)中:

services.AddSingleton<IAuthorizationHandler, BadgeEntryHandler>(); services.AddSingleton<IAuthorizationHandler, TemporaryStickerHandler>();

確保兩個(gè)處理程序都已注冊(cè)。如果某個(gè)處理程序在某一策略評(píng)估后使用context.succeed()來(lái)成功 BuildingEntryRequirement ,則策略評(píng)估將成功。但是當(dāng)我們調(diào)用context.Fail()方法后會(huì)將授權(quán)結(jié)構(gòu)設(shè)置失敗,那樣的話(huà),最后的結(jié)果都是會(huì)授權(quán)失敗的。所以正常情況下。我們都是只設(shè)置標(biāo)記context.succeed()。

四、說(shuō)明

這里對(duì)上文源碼中出現(xiàn)的一些聲明方法進(jìn)行說(shuō)明。

4.1 IAuthorizeData

使用 IAuthorizeDate 接口方法。定義授權(quán)規(guī)則應(yīng)用于資源所需的數(shù)據(jù)集。

public interface IAuthorizeData {string Policy { get; set; }string Roles { get; set; }string AuthenticationSchemes { get; set; } }

Policy:獲取或設(shè)置確定對(duì)資源的訪問(wèn)的策略名稱(chēng)。

Roles: ? ? ?獲取或設(shè)置以逗號(hào)分隔的允許訪問(wèn)資源的角色列表。

AuthenticationSchemes: ? 獲取或以設(shè)置以逗號(hào)分隔的方案列表,從中可以構(gòu)造用戶(hù)信息。

所以IAuthorizeData中定義的policy、roles、AuthenticationSchemes三個(gè)分別代表著授權(quán)系統(tǒng)中的三種授權(quán)方式。

具體的使用在后續(xù)講解授權(quán)的執(zhí)行流程中會(huì)進(jìn)行詳細(xì)介紹。

五、后續(xù)

上面主要講解了授權(quán)在配置方面的源碼,本來(lái)打算繼續(xù)接著往下寫(xiě)的,但是考慮到整體篇幅可能會(huì)太長(zhǎng)了,不便于閱讀。

所以授權(quán)揭秘的上篇內(nèi)容就說(shuō)到這里了,在后續(xù)的文章中,會(huì)繼續(xù)深入了解授權(quán)內(nèi)部機(jī)制的奧秘以及是如何實(shí)現(xiàn)執(zhí)行授權(quán)流程的。

六、總結(jié)

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

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

  • 參考的文檔 和官方源碼

  • 往期精彩回顧

    總結(jié)

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

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