.net core 自定義授權(quán)策略提供程序進(jìn)行權(quán)限驗(yàn)證
在這之前先了解一下鑒權(quán)和授權(quán)的概念;
鑒權(quán)
鑒權(quán)可以說(shuō)是身份驗(yàn)證,身份驗(yàn)證是確定用戶身份的過(guò)程;
在ASP.NET Core 中身份驗(yàn)證是由身份驗(yàn)證服務(wù)IAuthenticationService負(fù)責(zé)的,它被身份驗(yàn)證中間件使用, 身份驗(yàn)證服務(wù)會(huì)使用已注冊(cè)的身份驗(yàn)證處理程序來(lái)完成與身份驗(yàn)證相關(guān)的操作。身份驗(yàn)證相關(guān)的操作包括:對(duì)用戶身份進(jìn)行驗(yàn)證,對(duì)未經(jīng)身份驗(yàn)證的用戶進(jìn)行資源訪問(wèn)時(shí)做出響應(yīng)。
身份驗(yàn)證處理程序及其配置選項(xiàng)
身份驗(yàn)證處理程序包括CookieAuthenticationHandler 和 JwtBearerHandler,身份驗(yàn)證處理程序的注冊(cè) 是在調(diào)用AddAuthentication之后擴(kuò)展方法AddJwtBearer 和 AddCookie 提供的
身份驗(yàn)證處理程序會(huì)由實(shí)現(xiàn)IAuthenticationService 接口的AuthenticationService 的AuthenticateAsync 方法去調(diào)用
授權(quán)
授權(quán)是確定用戶是否有權(quán)訪問(wèn)資源的過(guò)程,這里先簡(jiǎn)單帶過(guò)一下后面接著講
授權(quán)方案
授權(quán)方案包括 基于角色的授權(quán),基于聲明的授權(quán),基于策略的授權(quán),這里著重說(shuō)一下策略授權(quán),
基于策略的授權(quán)
授權(quán)策略包含一個(gè)或多個(gè)要求
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AtLeast21", policy =>
policy.Requirements.Add(new MinimumAgeRequirement(21)));
});
在前面的示例中,創(chuàng)建了“AtLeast21”策略。該策略有一個(gè)最低年齡要求,其作為要求的參數(shù)提供
IAuthorizationRequirement
IAuthorizationRequirement用于跟蹤授權(quán)是否成功的機(jī)制
在IAuthorizationHandler 的 HandleAsync方法中 作為參數(shù)被調(diào)用,由HttpContext 的Requirements 屬性提供
IAuthorizationHandler
IAuthorizationHandler 用于檢查策略是否滿足要求,主要執(zhí)行的方法是HandleAsync,我們可以繼承微軟提供的AuthorizationHandler,默認(rèn)實(shí)現(xiàn)了HandlAsync ,在具有多個(gè)IAuthorizationRequirement 的情況下默認(rèn)是循環(huán)去執(zhí)行HandleRequirementAsync方法,在某些情況下我們可以去重寫從而去執(zhí)行特定IAuthorizationRequirement,當(dāng)然方法多樣
public virtual async Task HandleAsync(AuthorizationHandlerContext context)
{
if (context.Resource is TResource)
{
foreach (var req in context.Requirements.OfType<TRequirement>())
{
await HandleRequirementAsync(context, req, (TResource)context.Resource);
}
}
}
IAuthorizationPolicyProvider
IAuthorizationPolicyProvider 自定義策略提供程序
繼承IAuthorizationPolicyProvider 需要去實(shí)現(xiàn)IAuthorizationPolicyProvider 三個(gè)方法,三個(gè)方法的執(zhí)行由我們我們的Authorize特性決定,并且Authorize特性的策略名稱會(huì)傳遞到 IAuthorizationPolicyProvider 的GetPolicyAsync 作為參數(shù)使用
public Task<AuthorizationPolicy?> GetPolicyAsync(string policyName)
{
var policy = new AuthorizationPolicyBuilder();
//添加鑒權(quán)方案
policy.AddAuthenticationSchemes("Bearer");
policy.AddAuthenticationSchemes(CookieAuthenticationDefaults.AuthenticationScheme);
if (policyName is null)
{
return Task.FromResult<AuthorizationPolicy>(null);
}
var authorizations = policyName.Split(',');
if (authorizations.Any())
{
//權(quán)限策略構(gòu)建器,添加自定義的AuthorizaRequirement
policy.AddRequirements(new AuthorizeRequirement(authorizations));
}
return Task.FromResult(policy.Build());
}
這里看一下Authorize特性相關(guān)的屬性
public class AuthorizeAttribute : Attribute, IAuthorizeData
{
/// <summary>
/// </summary>
public AuthorizeAttribute() { }
/// <summary>
/// 初始化類類實(shí)例并且設(shè)置可訪問(wèn)資源策略名稱
/// </summary>
/// <param name="policy">The name of the policy to require for authorization.</param>
public AuthorizeAttribute(string policy)
{
Policy = policy;
}
/// <summary>
/// 設(shè)置或獲取可以訪問(wèn)資源策略名稱
/// </summary>
public string? Policy { get; set; }
/// <summary>
/// 設(shè)置或獲取可以訪問(wèn)資源的角色
/// </summary>
public string? Roles { get; set; }
/// <summary>
/// 設(shè)置或獲取可以訪問(wèn)資源鑒權(quán)方案名稱
/// </summary>
public string? AuthenticationSchemes { get; set; }
}
GetDefaultPolicyAsync
默認(rèn)策略,當(dāng)我們的Authorize特性上不提供策略時(shí)執(zhí)行,這里的CustomAuthorization特性是繼承了Authorize特性
[]
[]
[]
public int SetNotOP()
{
throw new ArgumentNullException(nameof(TestTask));
return 1;
}
GetPolicyAsync
在Authorize添加策略時(shí)執(zhí)行
[]
[]
public async Task<int> TestTask()
{
await Task.CompletedTask;
return 1;
}
GetFallbackPolicyAsync
后備授權(quán)策略,是指在沒(méi)有為請(qǐng)求指定其他策略時(shí),由授權(quán)中間件提供的策略。在這里可以指返回空值,也可以設(shè)置指定策略返回
public Task<AuthorizationPolicy GetFallbackPolicyAsync()
{
return Task.FromResult<AuthorizationPolicy>(null);
}
//或者
public Task<AuthorizationPolicy GetFallbackPolicyAsync()
{
var policy = new AuthorizationPolicyBuilder();
policy.AddAuthenticationSchemes("Bearer");
policy.AddAuthenticationSchemes(CookieAuthenticationDefaults.AuthenticationScheme);
return Task.FromResult<AuthorizationPolicy>(policy.Build());
}
IAuthorizationService
IAuthorizationService是確認(rèn)授權(quán)成功與否的主要服務(wù),兵器 負(fù)責(zé)去執(zhí)行我們自定義的AuthorizationHandle
看一段由微軟官方簡(jiǎn)化授權(quán)服務(wù)的代碼,可以看到AuthorizeAsync會(huì)去循環(huán)執(zhí)行自定義的AuthorizationHandle
public async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
object resource, IEnumerable<IAuthorizationRequirement> requirements)
{
// Create a tracking context from the authorization inputs.
var authContext = _contextFactory.CreateContext(requirements, user, resource);
// By default this returns an IEnumerable<IAuthorizationHandlers> from DI.
var handlers = await _handlers.GetHandlersAsync(authContext);
// Invoke all handlers.
foreach (var handler in handlers)
{
await handler.HandleAsync(authContext);
}
// Check the context, by default success is when all requirements have been met.
return _evaluator.Evaluate(authContext);
}
到這里應(yīng)該對(duì)整個(gè)授權(quán)流程有了個(gè)大致的了解,在授權(quán)前會(huì)由鑒權(quán)中間件進(jìn)行一個(gè)鑒權(quán),鑒權(quán)通過(guò)后由IAuthorizationPolicyProvider 來(lái)提供一個(gè)授權(quán)策略(授權(quán)策略里可以添加我們需要的IAuthorizationRequirement),最后由IAuthorizationService 的HandleAsync去執(zhí)行自定義AuthorizeHandle
具體實(shí)現(xiàn)
自定義特性
public class CustomAuthorizationAttribute : AuthorizeAttribute
{
public virtual string[] AuthorizeName { get; private set; }
public CustomAuthorizationAttribute(params string[] authorizeName)
{
AuthorizeName = authorizeName;
Policy = string.Join(",", AuthorizeName);
}
}
自定義策略提供程序
public class AuthorizationProvider : IAuthorizationPolicyProvider
{
public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
{
var policy = new AuthorizationPolicyBuilder();
policy.AddAuthenticationSchemes("Bearer");
policy.AddAuthenticationSchemes(CookieAuthenticationDefaults.AuthenticationScheme);
//默認(rèn)授權(quán)測(cè)率必須添加一個(gè)IAuthorizationRequirement的實(shí)現(xiàn)
policy.AddRequirements(new AuthorizeRequirement());
return Task.FromResult<AuthorizationPolicy>(policy.Build());
}
public Task<AuthorizationPolicy?> GetFallbackPolicyAsync()
{
return Task.FromResult<AuthorizationPolicy>(null);
}
public Task<AuthorizationPolicy?> GetPolicyAsync(string policyName)
{
var policy = new AuthorizationPolicyBuilder();
policy.AddAuthenticationSchemes("Bearer");
policy.AddAuthenticationSchemes(CookieAuthenticationDefaults.AuthenticationScheme);
if (policyName is null)
{
return Task.FromResult<AuthorizationPolicy>(null);
}
var authorizations = policyName.Split(',');
if (authorizations.Any())
{
policy.AddRequirements(new AuthorizeRequirement(authorizations));
}
return Task.FromResult(policy.Build());
}
}
自定義授權(quán)處理程序
IPermissionsCheck 是我注入的權(quán)限檢測(cè)程序,其實(shí)對(duì)于權(quán)限認(rèn)證,重要的是控制對(duì)資源的訪問(wèn),整篇文章下來(lái)無(wú)非就是將特性上的值提供到我們所需要進(jìn)行權(quán)限檢測(cè)的程序中去,當(dāng)然我們也可以用權(quán)限過(guò)濾器反射獲取Authorize特性上的值來(lái)實(shí)現(xiàn)
public class AuthorizeHandler : AuthorizationHandler<AuthorizeRequirement>
{
private readonly IPermissionCheck _permisscheck;
private readonly IHttpContextAccessor _httpContextAccessor;
public AuthorizeHandler(IHttpContextAccessor httpContextAccessor
, IServiceProvider serviceProvider)
{
using var scope = serviceProvider.CreateScope();
_permisscheck = scope.ServiceProvider.GetRequiredService<IPermissionCheck>();
_httpContextAccessor = httpContextAccessor;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, AuthorizeRequirement requirement)
{
var identity = _httpContextAccessor?.HttpContext?.User?.Identity;
var httpContext = _httpContextAccessor?.HttpContext;
var isAuthenticated = identity?.IsAuthenticated ?? false;
var claims = _httpContextAccessor?.HttpContext?.User?.Claims;
var userId = claims?.FirstOrDefault(p => p.Type == "Id")?.Value;
//判斷是否通過(guò)鑒權(quán)中間件--是否登錄
if (userId is null || !isAuthenticated)
{
context.Fail();
return;
}
var defaultPolicy = requirement.AuthorizeName?.Any() ?? false;
//默認(rèn)授權(quán)策略
if (!defaultPolicy)
{
context.Succeed(requirement);
return;
}
var roleIds = claims?
.Where(p => p?.Type?.Equals("RoleIds") ?? false)
.Select(p => long.Parse(p.Value));
var roleNames = claims?
.Where(p => p?.Type?.Equals(ClaimTypes.Role) ?? false)
.Select(p => p.Value);
UserTokenModel tokenModel = new UserTokenModel()
{
UserId = long.Parse(userId ?? "0"),
UserName = claims?.FirstOrDefault(p => p.Type == ClaimTypes.Name)?.Value ?? "",
RoleNames = roleNames?.ToArray(),
RoleIds = roleIds?.ToArray(),
};
if (requirement.AuthorizeName.Any())
{
if (!_permisscheck.IsGranted(tokenModel, requirement.AuthorizeName))
{
context.Fail();
return;
}
}
context.Succeed(requirement);
}
}
自定義IAuthorizationRequirement
public class AuthorizeRequirement : IAuthorizationRequirement
{
public virtual string[] AuthorizeName { get; private set; }
public AuthorizeRequirement(params string[] authorizeName)
{
AuthorizeName = authorizeName;
}
public AuthorizeRequirement() { }
}
自定義授權(quán)結(jié)果中間件
自定義授權(quán)結(jié)果中間件的作用,返回自定義響應(yīng),增強(qiáng)默認(rèn)質(zhì)詢或禁止響應(yīng)
public class AuthorizeMiddleHandle : IAuthorizationMiddlewareResultHandler
{
public async Task HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult)
{
if (!authorizeResult.Succeeded || authorizeResult.Challenged)
{
var isLogin = context?.User?.Identity?.IsAuthenticated ?? false;
var path = context?.Request?.Path ?? "";
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
var response = new AjaxResponse();
response.UnAuthorizedRequest = true;
response.StatusCode = "401";
var error = new ErrorInfo();
error.Error = isLogin ? $"你沒(méi)有權(quán)限訪問(wèn)該接口-接口路由{path}" : "請(qǐng)先登錄系統(tǒng)";
response.Error = error;
await context.Response.WriteAsJsonAsync(response);
return;
}
await next(context);
}
}
相關(guān)服務(wù)的注冊(cè)
context.Services.AddScoped<IPermissionCheck, PermissionCheck>();
context.Services.AddSingleton<IAuthorizationPolicyProvider, AuthorizationProvider>();
context.Services.AddSingleton<IAuthorizationMiddlewareResultHandler, AuthorizeMiddleHandle>();
????????????context.Services.AddSingleton<IAuthorizationHandler,?AuthorizeHandler>();
到這里對(duì)于權(quán)限認(rèn)證有了個(gè)大概的了解,至于是通過(guò)自定義策略提供程序自定義AuthorizHandle這一系列復(fù)雜的操作還是通過(guò)權(quán)限過(guò)濾器取決看官自己。個(gè)人認(rèn)為通過(guò)自定義策略提供程序自定義AuthorizHandle這種方式更靈活性,能夠應(yīng)對(duì)更多復(fù)雜場(chǎng)景。
作者:阿文不知所措
原文鏈接:cnblogs.com/lonely-wen/p/17247847.html
版權(quán)聲明:本文來(lái)源于網(wǎng)友收集或網(wǎng)友供稿,僅供學(xué)習(xí)交流之用,如果有侵權(quán),請(qǐng)轉(zhuǎn)告小編或者留言,本公眾號(hào)立即刪除。
支持小薇
關(guān)注公眾號(hào)↑↑↑:DotNet開(kāi)發(fā)跳槽?????
點(diǎn) 分 享

點(diǎn) 收 藏

點(diǎn) 點(diǎn) 贊
點(diǎn)在看
