没有内置函数可以做到这一点。
但您可以轻松实现自定义LogicalOrPolicyProvider(也是一个处理程序)来实现相同的目标。 LogicalOrPolicyProvider会根据策略名称动态构造策略,例如:
[Authorize(Policy="Choice: policy='New York'| role= ADMIN")]
上述属性将生成一个新策略,该策略应满足“纽约”策略或需要ADMIN 角色
此外,我们可以定义一些规则来处理更通用的情况。假设要编写以下要求:
Choice: policy='New York'| role= ADMIN
Choice: policy='New York'| role= 'ADMIN'
Choice: policy='New York'| policy = 'WC' | role= root | role = 'GVN'
您可以根据自己的喜好定义自己的规则,我个人更喜欢:
- 以标记
Choice 开头,后跟分隔符:(可能有几个可选的空格字符' ')
- 一个策略由
policy=policyName定义,如果policyName包含空格,你应该用''包围它。您可以根据需要定义多个policy
- 角色由
role = roleName 定义。您还可以定义任意数量的角色。
- 所有
policy 和role 定义都由| 分隔。
上述设计的实现:
让我们定义一个LogicalOrRequirement 来保存所有可能的策略:
public class LogicalOrRequirement : IAuthorizationRequirement
{
public IList<AuthorizationPolicy> Policies { get; }
public LogicalOrRequirement(IList<AuthorizationPolicy> policies)
{
this.Policies = policies;
}
}
如果这些策略中的任何一个成功了,只需绕过:
public class LogicalOrAuthorizationHandler : AuthorizationHandler<LogicalOrRequirement>
{
public LogicalOrAuthorizationHandler(IHttpContextAccessor httpContextAccessor)
{
this._httpContextAccessor = httpContextAccessor;
}
private readonly IHttpContextAccessor _httpContextAccessor;
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, LogicalOrRequirement requirement)
{
var httpContext = this._httpContextAccessor.HttpContext;
var policyEvaluator = httpContext.RequestServices.GetRequiredService<IPolicyEvaluator>();
foreach (var policy in requirement.Policies)
{
var authenticateResult = await policyEvaluator.AuthenticateAsync(policy, httpContext);
if (authenticateResult.Succeeded)
{
context.Succeed(requirement);
}
}
}
}
现在让我们通过PolicyProvider动态构建策略:
public class LogicalOrPolicyProvider : IAuthorizationPolicyProvider
{
const string POLICY_PREFIX = "Choice";
const string TOKEN_POLICY="policy";
const string TOKEN_ROLE="role";
public const string Format = "Choice: policy='p3' | policy='p2' | role='role1' | ...";
private AuthorizationOptions _authZOpts { get; }
public DefaultAuthorizationPolicyProvider FallbackPolicyProvider { get; }
public LogicalOrPolicyProvider(IOptions<AuthorizationOptions> options )
{
_authZOpts = options.Value;
FallbackPolicyProvider = new DefaultAuthorizationPolicyProvider(options);
}
// Choice: policy= | policy= | role= | role = ...
public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
{
if (policyName.StartsWith(POLICY_PREFIX, StringComparison.OrdinalIgnoreCase))
{
var policyNames = policyName.Substring(POLICY_PREFIX.Length);
var startIndex = policyNames.IndexOf(":");
if(startIndex == -1 || startIndex == policyNames.Length)
{
throw new ArgumentException($"invalid syntax, must contains a ':' before tokens. The correct format is {Format}");
}
// skip the ":" , and turn it into the following list
// [[policy,policyName],[policy,policName],...[role,roleName],...,]
var list= policyNames.Substring(startIndex+1)
.Split("|")
.Select(p => p.Split("=").Select(e => e.Trim().Trim('\'')).ToArray() )
;
// build policy for roleNames
var rolesPolicyBuilder = new AuthorizationPolicyBuilder();
var roleNames =list.Where(arr => arr[0].ToLower() == TOKEN_ROLE)
.Select(arr => arr[1])
.ToArray();
var rolePolicy = rolesPolicyBuilder.RequireRole(roleNames).Build();
// get policies with all related names
var polices1= list.Where(arr => arr[0].ToLower() == TOKEN_POLICY);
var polices=polices1
.Select(arr => arr[1])
.Select(name => this._authZOpts.GetPolicy(name)) // if the policy with the name doesn exit => null
.Where(p => p != null) // filter null policy
.Append(rolePolicy)
.ToList();
var pb= new AuthorizationPolicyBuilder();
pb.AddRequirements(new LogicalOrRequirement(polices));
return Task.FromResult(pb.Build());
}
return FallbackPolicyProvider.GetPolicyAsync(policyName);
}
public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
{
return FallbackPolicyProvider.GetDefaultPolicyAsync();
}
}
最后,别忘了在Startup.cs 中注册这两个服务:
services.AddSingleton<IAuthorizationPolicyProvider, LogicalOrPolicyProvider>();
services.AddSingleton<IAuthorizationHandler, LogicalOrAuthorizationHandler>();
现在,只要你想对or 进行逻辑组合,只需添加一个[Authorize(Policy="Choice: ...")]:
[Authorize(Policy="Choice: policy='New York'| role= ADMIN")]
public IActionResult Privacy()
{
return View();
}