在业务开发做权限管理的时候,会遇到同一个Service方法,会有多个授权的情况,这时候就要添加多个AuthorizeAttribute,类似下面这样:
/// <summary>
/// 获取详情
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[Authorize(Permissions.Edit)]
[Authorize(Permissions.View)]
public async Task<Result> Get(Guid id)
{
//.....
}
在abp vnext框架中,进行权限校验时,是以且的方式进行,即用户必须同时AuthorizeAttribute的权限才能通过校验,否则就就无权限,返回403状态码。
在博客园找到一篇文章 《abp 以或的方式验证多个 AuthorizeAttribute》,按他的方法,发现只有在使用controller再调用Service层的方法,并且AuthorizeAttribute写在Service层方法上时,才会触发该方法;如果使用自动api的方式,依然是403错误;
通过查看abp源码,看AddAlwaysAllowAuthorization 的实现方法后,发现权限校验入口是IAuthorizationService接口中下面这个方法,那么只要对这个方法进行调整就可以实现了。
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object? resource, IEnumerable<IAuthorizationRequirement> requirements)
abp vnext 中,该接口注入的是AbpAuthorizationService这个类,则继承该类,重写AuthorizeAsync方法,将参数requirements中,属于权限策略的部分提取出来。先用原来的方法,校验除权限策略外的其他需求规则,校验不通过,则直接返回,无需再校验权限;否则再校验权限策略,如果校验不通过,则比较不通过的数量和权限策略的数量,如果小于,则表示有部分策略通过了校验,那么改写校验结果即可。如此,便实现了以“或”的方法验证多个Authorize;
具体实现代码如下:
/// <summary>
/// 改写鉴权方法,实现多个Authorize特性,以“或”的方式进行鉴权
/// </summary>
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IAuthorizationService), typeof(IAbpAuthorizationService))]
public class MyAuthorizationService : AbpAuthorizationService, ITransientDependency
{
public MyAuthorizationService(IAuthorizationPolicyProvider policyProvider, IAuthorizationHandlerProvider handlers, ILogger<DefaultAuthorizationService> logger, IAuthorizationHandlerContextFactory contextFactory, IAuthorizationEvaluator evaluator, IOptions<AuthorizationOptions> options, ICurrentPrincipalAccessor currentPrincipalAccessor, IServiceProvider serviceProvider)
: base(policyProvider, handlers, logger, contextFactory, evaluator, options, currentPrincipalAccessor, serviceProvider)
{
}
/// <summary>
/// 重写授权方法,将策略从需求规则中拆分出来,单独鉴权
/// </summary>
/// <param name="user"></param>
/// <param name="resource"></param>
/// <param name="requirements"></param>
/// <returns></returns>
public override async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object? resource, IEnumerable<IAuthorizationRequirement> requirements)
{
//如果权限策略为空,则直接返回成功
if (requirements.Count() == 0)
{
return AuthorizationResult.Success();
}
//权限策略需求规则
var permissionRequirements = requirements.Where(p => p.ToString()!.StartsWith("PermissionRequirement:")).ToList();
if (permissionRequirements.Count > 0)
{
//从需求规则中剔除权限策略
requirements = requirements.Where(p => !permissionRequirements.Any(q => q.Equals(p))).ToList();
}
//校验除权限策略外的其他需求规则
if (requirements.Count() > 0)
{
var result = await base.AuthorizeAsync(user, resource, requirements);
//如果校验失败,则直接返回,无需再校验权限规则
if (!result.Succeeded)
{
return result;
}
}
//校验权限策略规则
if (permissionRequirements.Count > 0)
{
var result = await base.AuthorizeAsync(user, resource, permissionRequirements);
//校验不通过,则检查是否全部策略都不通过,如果不是,则认为通过校验,改写校验结果
if (!result.Succeeded && result.Failure!.FailedRequirements.Count() < permissionRequirements.Count)
{
result = AuthorizationResult.Success();
}
return result;
}
return AuthorizationResult.Success();
}
}