一、前言
回顧:認證授權方案之授權初識
從上一節中,我們在對授權系統已經有了初步的認識和使用,可以發現,asp.net core為我們提供的授權策略是一個非常強大豐富且靈活的認證授權方案,能夠滿足大部分的授權場景,
在ConfigureServices中配置服務:將授權服務添加到容器
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthorization(options =>
{
options.AddPolicy("customizePermisson",
policy => policy
.Requirements
.Add(new PermissionRequirement("user")));
});
//此外,還需要在 IAuthorizationHandler 型別的范圍內向 DI 系統注冊新的處理程式:
services.AddScoped<IAuthorizationHandler, PermissionRequirementHandler>();
}
在Configure中注冊管道:運行使用呼叫方法來配置Http請求管道
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//開啟授權
app.UseAuthorization();
}
通過以上幾行代碼的實作,就可以進行授權了,這個時候,你可以會問,這幾行代碼都進行了什么操作實作授權的?
好了,繼續回到上節最后說的在這一節中對授權策略的核心進行一步步的揭秘的,

二、開始
引入整體結構

2.1 添加授權AddAuthorization
添加授權策略服務使用AddAuthorization方法,以便呼叫,
從原始碼可以發現,從core3.0后,由之前在core2.0中的AuthorizationServiceCollectionExtensions.cs檔案中,原來的AddAuthorization的方法變為了AddAuthorizationCore方法,微軟在這一塊進行了封裝在PolicyServiceCollectionExtensions.cs檔案中,沿用了之前AddAuthorization拓展名稱,不影響之前版本的使用,
我們來看看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();
}
}
由上可知,在呼叫AddAuthorization方法進行授權配置的時候,需要使用到AuthorizationOptions委托方式傳參,
所以我們再來看看下面這一行代碼,通過AddPolicy實作添加策略方式,
options.AddPolicy("customizePermisson",policy => policy.Requirements.Add(new PermissionRequirement("user")));
查看原始碼發現是參考了AuthorizationOptions物件,
2.2 配置選項AuthorizationOptions
授權選項實作添加和授權配置,提供授權服務的配置,
原始碼如下:
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;
}
}
定義一個字典
private Dictionary<string, AuthorizationPolicy> PolicyMap { get; } = new Dictionary<string, AuthorizationPolicy>(StringComparer.OrdinalIgnoreCase);
目的在于將定義的授權策略方式都保存在這個宣告的PolicyMap當中,而其中AddPolicy方法是將配置的策略添加到字典中,
public void AddPolicy(string name, AuthorizationPolicy policy);
public void AddPolicy(string name, Action<AuthorizationPolicyBuilder> configurePolicy);
而這方法中涉及到兩種不同的傳參物件AuthorizationPolicy和AuthorizationPolicyBuilder,
2.3 授權策略 AuthorizationPolicy
表示授權要求和方案的集合,具體原始碼如下:
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 =https://www.cnblogs.com/i3yuan/p/= null)
{
throw new ArgumentNullException(nameof(authorizeData));
}
// Avoid allocating enumerator if the data is known to be empty
var skipEnumeratingData = false;
if (authorizeData is IList 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 one
if (policyBuilder == null)
{
var fallbackPolicy = await policyProvider.GetFallbackPolicyAsync();
if (fallbackPolicy != null)
{
return fallbackPolicy;
}
}
return policyBuilder?.Build();
}
}
我們從原始碼中可以發現,Authorization 物件 Combine方法目的在于將授權策略進行合并,同時呼叫了AuthorizationPolicyBuilder物件中Combine方法,對授權方案或者授權策略進行合并,再來看看AuthorizationPolicy物件中的CombineAsync方法,這里的引數用到了IAuthorizeData,同時這個方法的程序是將可能基于角色,基于方案或者基于策略都合并轉換為是授權策略的方式,也是通過呼叫AuthorizationPolicyBuilder物件來實作合并, 所以可以看得出AuthorizationPolicyBuilder提供了一些創建AuthorizationPolicy的方法,
這個時候,我們可以發現,其實之前說的基于角色、基于方案的授權方式本質上來說都是基于策略授權,
2.4 構建策略AuthorizationPolicyBuilder
除了上面說到使用AuthorizationPolicy物件之外,我們還可以用AuthorizationPolicyBuilder物件以Buider來創建AuthorizationPolicy物件,將多個AuthorizationPolicy物件提供的陣列進行合并,所以AuthorizationPolicyBuilder提供的Combine方法的使用,為AuthorizationPolicy授權構建提供了許多便捷的方式,
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());
}
}
由上面多出出現的IAuthorizationRequirement物件可以發現,授權要求Requirement屬性是策略的核心方案,每一種Requirement都代表一種授權方式,同時IAuthorizationPolicyBuilder為這些預定義的方案創建了它們對應的使用方式并將其添加到Requirements集合中,
2.5 授權要求IAuthorizationRequirement
public interface IAuthorizationRequirement
{
}
介面并沒有任何實作成員,因為授權要求是具有不同的表現形式的,所有才沒有具體的實作成員,授權要求目的在于檢驗某個當前用戶是否具有相應的要求, 所以大部分IAuthorizationRequirement介面的實作類都繼承了IAuthorizationHandler 介面來提供HandleAsync方法來實作對應的授權檢驗,
下面介紹asp.net core框架里面默認實作的幾種IAuthorizationRequirement實作型別,
2.5.1 DenyAnonymousAuthorizationRequirement
阻止匿名用戶操作,言外之意就是拒絕未被驗證的匿名用戶訪問資源,
原始碼如下:
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;
}
}
通過用戶的CliamPrincipal物件身份是否為慷訓是否是一個經過認證的用戶身份,以此來確定當前請求的用戶是否來源于匿名用戶,
2.5.2 NameAuthorizationRequirement
指定用戶名的授權方式,判斷當前用戶與某個指定的用戶是否匹配以此來授權訪問資源,
原始碼如下:
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屬性為授權用戶,通過HandleRequirementAsync方法進行校驗當前用戶的ClaimPrincipal物件的身份與RequiredName是否具有匹配,
這里的判斷用的是 string.Equals() 說明這里比較的用戶名是區別大小寫的,
2.5.3 ClaimsAuthorizationRequirement
基于指定宣告型別的授權策略,檢驗當前用戶是否宣告型別和候選值,
原始碼如下:
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這兩個屬性在建構式中被初始化,分別用來表示當前宣告的宣告型別和默認允許值,通過HandleRequirementAsync來授權檢驗是否完成通過,
2.5.4 RolesAuthorizationRequirement
基于角色的授權策略,檢驗當前用戶是否擁有約定匹配的角色,如果擁有,則可以訪問對應的資源,
原始碼如下:
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表示目標角色串列的集合,通過HandleRequirementAsync實作授權檢驗,呼叫IsInRole方法來判斷當前用戶的ClaimsPrincipal物件是否有指定的角色,
2.5.5 AssertionRequirement
基于AuthorizationHandlerContext背景關系斷言的形式來宣告授權,
原始碼如下:
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);
}
}
}
通過型別為Func<AuthorizationHandlerContext, Task<bool>>的委托來表示該斷言,利用它來授權驗證,在HandleAsync檢驗方法中,直接呼叫這個委托物件來完成判斷,
2.5.6 OperationAuthorizationRequirement
基于預定義操作的授權策略,
原始碼如下:
public class OperationAuthorizationRequirement : IAuthorizationRequirement
{
public string Name { get; set; }
}
由上可知,只是包含一個操作名字的Name屬性,目的在于將授權的目標物件映射到一個預定義的操作上,
三、用例
出現的IAuthorizationRequirement物件可以發現,授權要求Requirement屬性是策略的核心方案,每一中Requirement都代表一種授權方式,
在上文我們通過構建策略AuthorizationPolicyBuilder物件的原始碼可以發現,為我們提供了多個方法由預定義的IAuthorizationRequirement型別來創建并將其添加到Requirements集合中,
3.1 應用
實體應用如下:在ConfigureServices中配置服務中
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
var combindPolicy = new AuthorizationPolicyBuilder().RequireClaim("role").Build();
services.AddAuthorization(options =>
{
//DenyAnonymousAuthorizationRequirement
options.AddPolicy("DenyAnonyUser", policy => policy.RequireAuthenticatedUser());
//NameAuthorizationRequirement
options.AddPolicy("NameAuth", policy => policy.RequireUserName("艾三元"));
//ClaimsAuthorizationRequirement
options.AddPolicy("ClaimsAuth", policy => policy.RequireClaim("role","admin"));
//RolesAuthorizationRequirement
options.AddPolicy("RolesAuth", policy => policy.RequireRole("admin","user"));
//AssertionRequirement
options.AddPolicy("AssertAuth", policy => policy.RequireAssertion(c=>c.User.HasClaim(o=>o.Type=="role")));
//同樣可可用直接呼叫Combind方法,策略AuthorizationPolicy
options.AddPolicy("CombindAuth", policy => policy.Combine(combindPolicy));
});
}
以上,分別實作了框架中默認實作的幾種IAuthorizationRequirement實作型別在實際中的應用,通過不同授權要求實作的策略方式,同時也可以將上面多種方式合并成一個物件,進行呼叫使用,
3.2 拓展
當然了,除了自帶了這幾種默認實作方式之外,我們也可以通過自定義Requirement來滿足我們的需求,
這個在上一節初識授權的時候,已經提到了自定義授權這一塊,所以在這里再看一次,
定義一個權限策略
PermissionRequirement,這個策略并包含一些屬性,public class PermissionRequirement: IAuthorizationRequirement { public string _permissionName { get; } public PermissionRequirement(string PermissionName) { _permissionName = PermissionName; } }再定義一個策略處理類
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 = https://www.cnblogs.com/i3yuan/p/role.Value; if (roleValue==requirement._permissionName) { context.Succeed(requirement); } } return Task.CompletedTask;配置使用
public void ConfigureServices(IServiceCollection services) { services.AddControllers(); //基于自定義策略授權 services.AddAuthorization(options => { options.AddPolicy("customizePermisson", policy => policy .Requirements .Add(new PermissionRequirement("admin"))); }); //此外,還需要在 IAuthorizationHandler 型別的范圍內向 DI 系統注冊新的處理程式: services.AddScoped<IAuthorizationHandler, PermissionRequirementHandler>(); // 如前所述,要求可包含多個處理程式,如果為授權層的同一要求向 DI 系統注冊多個處理程式,有一個成功就足夠了, }
特別說明
上述使用的處理程式是一對一的關系,當宣告要求滿足條件的時候,則任務授權成功, 授權成功后,
context.Succeed將通過滿足要求作為其唯一引數呼叫,但是授權策略中也包含一對多的要求關系,它們屬于 & 的關系,只用全部驗證通過,才能最終授權成功,但是在有些場景下,我們可能希望一個授權策略可以適用多種情況,比如,我們進入公司時需要出示員工卡才可以被授權進入,但是如果我們忘了帶員工卡,可以去申請一個臨時卡,同樣可以授權成功,
這里貼一個官方檔案的寫法: 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; } }
我們定義了兩個Handler,但是想讓它們得到執行,還需要將其注冊到DI系統中:
services.AddSingleton<IAuthorizationHandler, BadgeEntryHandler>();
services.AddSingleton<IAuthorizationHandler, TemporaryStickerHandler>();
確保兩個處理程式都已注冊, 如果某個處理程式在某一策略評估后使用context.succeed()來成功 BuildingEntryRequirement ,則策略評估將成功,但是當我們呼叫context.Fail()方法后會將授權結構設定失敗,那樣的話,最后的結果都是會授權失敗的,所以正常情況下,我們都是只設定標記context.succeed(),
四、說明
這里對上文原始碼中出現的一些宣告方法進行說明,
4.1 IAuthorizeData
使用 IAuthorizeDate 介面方法,定義授權規則應用于資源所需的資料集,
public interface IAuthorizeData
{
string Policy { get; set; }
string Roles { get; set; }
string AuthenticationSchemes { get; set; }
}
Policy:獲取或設定確定對資源的訪問的策略名稱,
Roles: 獲取或設定以逗號分隔的允許訪問資源的角色串列,
AuthenticationSchemes: 獲取或以設定以逗號分隔的方案串列,從中可以構造用戶資訊,
所以IAuthorizeData中定義的policy、roles、AuthenticationSchemes三個分別代表著授權系統中的三種授權方式,
具體的使用在后續講解授權的執行流程中會進行詳細介紹,
五、后續
上面主要講解了授權在配置方面的原始碼,本來打算繼續接著往下寫的,但是考慮到整體篇幅可能會太長了,不便于閱讀,
所以授權揭秘的上篇內容就說到這里了,在后續的文章中,會繼續深入了解授權內部機制的奧秘以及是如何實作執行授權流程的,
六、總結
- 從添加授權配置開始,我們引入了需要的授權配置選項,而不同的授權要求構建不同的策略方式,從而實作一種自己滿意的授權需求配置要求,
- 如果有不對的或不理解的地方,希望大家可以多多指正,提出問題,一起討論,不斷學習,共同進步,
- 參考的檔案 和官方原始碼
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/4889.html
標籤:.NET Core
下一篇:遵守這些原則讓你開發效率提高一倍
