ASP.NET 5 授权针对两个或多个策略(或组合策略)
ASP.NET 5 Authorize against two or more policies (OR-combined policy)
是否可以针对两个或多个策略申请授权?我正在使用 ASP.NET 5, rc1.
[Authorize(Policy = "Limited,Full")]
public class FooBarController : Controller
{
// This code doesn't work
}
如果没有,我如何在不使用政策的情况下实现这一目标?有两组用户可以访问此控制器:"Full" 和 "Limited"。用户可能属于 "Full" 或 "Limited",或两者兼而有之。他们只需要属于两个组之一即可访问此控制器。
不是你想要的方式;政策旨在累积。例如,如果您使用两个单独的属性,那么它们都必须通过。
您必须在单个策略中评估 OR 条件。 但是您不必在单个处理程序中将其编码为 OR。您可以有一个具有多个处理程序的需求。如果任一处理程序标记成功,则满足要求。请参阅我的 Authorization Workshop.
中的第 6 步
设置新政策后"LimitedOrFull"(假设它们与声明类型名称匹配)创建如下要求:
options.AddPolicy("LimitedOrFull", policy =>
policy.RequireAssertion(context =>
context.User.HasClaim(c =>
(c.Type == "Limited" ||
c.Type == "Full"))));
Net Core 可以选择拥有多个具有相同 AuthorizationRequirement 类型的 AuthorizationHandler。只有其中一个必须成功才能通过授权
https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-2.1#why-would-i-want-multiple-handlers-for-a-requirement
我使用策略和角色:
[Authorize(Policy = "ManagerRights", Roles = "Administrator")]
使用动态创建需求的解决方案最适合我:
- 创建单独的“有限”和“完整”政策要求的界面:
public interface ILimitedRequirement : IAuthorizationRequirement { }
public interface IFullRequirement : IAuthorizationRequirement { }
- 为授权创建自定义属性:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class AuthorizeAnyAttribute : AuthorizeAttribute {
public string[] Policies { get; }
public AuthorizeAnyAttribute(params string[] policies) : base(String.Join("Or", policies))
=> Policies = policies;
}
- 为
ILimitedRequirement
和 IFullRequirement
创建授权处理程序(请注意,这些处理程序处理 接口 ,而不是 类):
public class LimitedRequirementHandler : AuthorizationHandler<ILimitedRequirement> {
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, ILimitedRequirement requirement) {
if(limited){
context.Succeed(requirement);
}
}
}
public class FullRequirementHandler : AuthorizationHandler<IFullRequirement> {
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, IFullRequirement requirement) {
if(full){
context.Succeed(requirement);
}
}
}
- 如果您的授权处理程序很繁重(例如,其中一个访问数据库)并且您不希望其中一个执行授权检查是否另一个已经成功或失败,您可以使用下一个解决方法(记住处理程序注册的顺序直接决定了它们在请求管道中的执行顺序):
public static class AuthorizationExtensions {
public static bool IsAlreadyDetermined<TRequirement>(this AuthorizationHandlerContext context)
where TRequirement : IAuthorizationRequirement
=> context.HasFailed || context.HasSucceeded
|| !context.PendingRequirements.Any(x => x is TRequirement);
}
public class LimitedRequirementHandler : AuthorizationHandler<ILimitedRequirement> {
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, ILimitedRequirement requirement) {
if(context.IsAlreadyDetermined<ILimitedRequirement>())
return;
if(limited){
context.Succeed(requirement);
}
}
}
public class FullRequirementHandler : AuthorizationHandler<IFullRequirement> {
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, IFullRequirement requirement) {
if(context.IsAlreadyDetermined<IFullRequirement>())
return;
if(full){
context.Succeed(requirement);
}
}
}
- 注册授权处理程序(注意没有“LimiterOrFullRequirementHandler”,这两个处理程序将处理组合策略要求):
//Order of handlers is important - it determines their execution order in request pipeline
services.AddScoped<IAuthorizationHandler, LimitedRequirementHandler>();
services.AddScoped<IAuthorizationHandler, FullRequirementHandler>();
- 现在我们需要检索所有
AuthorizeAny
属性并使用 ImpromptuInterface(或任何其他用于动态创建类型实例的工具)为它们动态创建需求:
using ImpromptuInterface;
List<AuthorizeAnyAttribute> attributes = new List<AuthorizeAnyAttribute>();
foreach(Type type in Assembly.GetExecutingAssembly().GetTypes().Where(type => type.IsAssignableTo(typeof(ControllerBase)))) {
attributes.AddRange(Attribute.GetCustomAttributes(type , typeof(AuthorizeAnyAttribute))
.Cast<AuthorizeAnyAttribute>()
.Where(x => x.Policy != null));
foreach(var methodInfo in type.GetMethods()) {
attributes.AddRange(Attribute.GetCustomAttributes(methodInfo , typeof(AuthorizeAnyAttribute))
.Cast<AuthorizeAnyAttribute>()
.Where(x => x.Policy != null));
}
}
//Add base requirement interface from which all requirements will be created on demand
Dictionary<string, Type> baseRequirementTypes = new();
baseRequirementTypes.Add("Limited", typeof(ILimitedRequirement));
baseRequirementTypes.Add("Full", typeof(IFullRequirement));
Dictionary<string, IAuthorizationRequirement> requirements = new();
foreach(var attribute in attributes) {
if(!requirements.ContainsKey(attribute.Policy)) {
Type[] requirementTypes = new Type[attribute.Policies.Length];
for(int i = 0; i < attribute.Policies.Length; i++) {
if(!baseRequirementTypes.TryGetValue(attribute.Policies[i], out Type requirementType))
throw new ArgumentException($"Requirement for {attribute.Policies[i]} policy doesn't exist");
requirementTypes[i] = requirementType;
}
//Creating instance of combined requirement dynamically
IAuthorizationRequirement newRequirement = new { }.ActLike(requirementTypes);
requirements.Add(attribute.Policy, newRequirement);
}
}
- 注册所有创建的需求
services.AddAuthorization(options => {
foreach(KeyValuePair<string, IAuthorizationRequirement> item in requirements) {
options.AddPolicy(item.Key, x => x.AddRequirements(item.Value));
}
}
如果默认 AuthorizeAttribute
与自定义 AuthorizeAnyAttribute
处理相同,则上述解决方案允许处理与 OR 组合相同的单一需求
如果上述解决方案有点矫枉过正,始终可以使用手动组合类型创建和注册:
- 创建组合的“有限或完整”策略要求:
public class LimitedOrFullRequirement : ILimitedRequirement, IFullRequirement { }
- 如果这两个要求也必须单独使用(除了使用组合的“有限或完整”策略),请为单个要求创建接口实现:
public class LimitedRequirement : ILimitedRequirement { }
public class FullRequirement : IFullRequirement { }
- 注册政策(请注意,注释掉的政策 完全可选 注册):
services.AddAuthorization(options => {
options.AddPolicy("Limited Or Full",
policy => policy.AddRequirements(new LimitedOrFullRequirement()));
//If these policies also have single use, they need to be registered as well
//options.AddPolicy("Limited",
// policy => policy.AddRequirements(new LimitedRequirement()));
//options.AddPolicy("Full",
// policy => policy.AddRequirements(new FullRequirement()));
});
是否可以针对两个或多个策略申请授权?我正在使用 ASP.NET 5, rc1.
[Authorize(Policy = "Limited,Full")]
public class FooBarController : Controller
{
// This code doesn't work
}
如果没有,我如何在不使用政策的情况下实现这一目标?有两组用户可以访问此控制器:"Full" 和 "Limited"。用户可能属于 "Full" 或 "Limited",或两者兼而有之。他们只需要属于两个组之一即可访问此控制器。
不是你想要的方式;政策旨在累积。例如,如果您使用两个单独的属性,那么它们都必须通过。
您必须在单个策略中评估 OR 条件。 但是您不必在单个处理程序中将其编码为 OR。您可以有一个具有多个处理程序的需求。如果任一处理程序标记成功,则满足要求。请参阅我的 Authorization Workshop.
中的第 6 步设置新政策后"LimitedOrFull"(假设它们与声明类型名称匹配)创建如下要求:
options.AddPolicy("LimitedOrFull", policy =>
policy.RequireAssertion(context =>
context.User.HasClaim(c =>
(c.Type == "Limited" ||
c.Type == "Full"))));
Net Core 可以选择拥有多个具有相同 AuthorizationRequirement 类型的 AuthorizationHandler。只有其中一个必须成功才能通过授权 https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-2.1#why-would-i-want-multiple-handlers-for-a-requirement
我使用策略和角色:
[Authorize(Policy = "ManagerRights", Roles = "Administrator")]
使用动态创建需求的解决方案最适合我:
- 创建单独的“有限”和“完整”政策要求的界面:
public interface ILimitedRequirement : IAuthorizationRequirement { }
public interface IFullRequirement : IAuthorizationRequirement { }
- 为授权创建自定义属性:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class AuthorizeAnyAttribute : AuthorizeAttribute {
public string[] Policies { get; }
public AuthorizeAnyAttribute(params string[] policies) : base(String.Join("Or", policies))
=> Policies = policies;
}
- 为
ILimitedRequirement
和IFullRequirement
创建授权处理程序(请注意,这些处理程序处理 接口 ,而不是 类):
public class LimitedRequirementHandler : AuthorizationHandler<ILimitedRequirement> {
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, ILimitedRequirement requirement) {
if(limited){
context.Succeed(requirement);
}
}
}
public class FullRequirementHandler : AuthorizationHandler<IFullRequirement> {
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, IFullRequirement requirement) {
if(full){
context.Succeed(requirement);
}
}
}
- 如果您的授权处理程序很繁重(例如,其中一个访问数据库)并且您不希望其中一个执行授权检查是否另一个已经成功或失败,您可以使用下一个解决方法(记住处理程序注册的顺序直接决定了它们在请求管道中的执行顺序):
public static class AuthorizationExtensions {
public static bool IsAlreadyDetermined<TRequirement>(this AuthorizationHandlerContext context)
where TRequirement : IAuthorizationRequirement
=> context.HasFailed || context.HasSucceeded
|| !context.PendingRequirements.Any(x => x is TRequirement);
}
public class LimitedRequirementHandler : AuthorizationHandler<ILimitedRequirement> {
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, ILimitedRequirement requirement) {
if(context.IsAlreadyDetermined<ILimitedRequirement>())
return;
if(limited){
context.Succeed(requirement);
}
}
}
public class FullRequirementHandler : AuthorizationHandler<IFullRequirement> {
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, IFullRequirement requirement) {
if(context.IsAlreadyDetermined<IFullRequirement>())
return;
if(full){
context.Succeed(requirement);
}
}
}
- 注册授权处理程序(注意没有“LimiterOrFullRequirementHandler”,这两个处理程序将处理组合策略要求):
//Order of handlers is important - it determines their execution order in request pipeline
services.AddScoped<IAuthorizationHandler, LimitedRequirementHandler>();
services.AddScoped<IAuthorizationHandler, FullRequirementHandler>();
- 现在我们需要检索所有
AuthorizeAny
属性并使用 ImpromptuInterface(或任何其他用于动态创建类型实例的工具)为它们动态创建需求:
using ImpromptuInterface;
List<AuthorizeAnyAttribute> attributes = new List<AuthorizeAnyAttribute>();
foreach(Type type in Assembly.GetExecutingAssembly().GetTypes().Where(type => type.IsAssignableTo(typeof(ControllerBase)))) {
attributes.AddRange(Attribute.GetCustomAttributes(type , typeof(AuthorizeAnyAttribute))
.Cast<AuthorizeAnyAttribute>()
.Where(x => x.Policy != null));
foreach(var methodInfo in type.GetMethods()) {
attributes.AddRange(Attribute.GetCustomAttributes(methodInfo , typeof(AuthorizeAnyAttribute))
.Cast<AuthorizeAnyAttribute>()
.Where(x => x.Policy != null));
}
}
//Add base requirement interface from which all requirements will be created on demand
Dictionary<string, Type> baseRequirementTypes = new();
baseRequirementTypes.Add("Limited", typeof(ILimitedRequirement));
baseRequirementTypes.Add("Full", typeof(IFullRequirement));
Dictionary<string, IAuthorizationRequirement> requirements = new();
foreach(var attribute in attributes) {
if(!requirements.ContainsKey(attribute.Policy)) {
Type[] requirementTypes = new Type[attribute.Policies.Length];
for(int i = 0; i < attribute.Policies.Length; i++) {
if(!baseRequirementTypes.TryGetValue(attribute.Policies[i], out Type requirementType))
throw new ArgumentException($"Requirement for {attribute.Policies[i]} policy doesn't exist");
requirementTypes[i] = requirementType;
}
//Creating instance of combined requirement dynamically
IAuthorizationRequirement newRequirement = new { }.ActLike(requirementTypes);
requirements.Add(attribute.Policy, newRequirement);
}
}
- 注册所有创建的需求
services.AddAuthorization(options => {
foreach(KeyValuePair<string, IAuthorizationRequirement> item in requirements) {
options.AddPolicy(item.Key, x => x.AddRequirements(item.Value));
}
}
如果默认 AuthorizeAttribute
与自定义 AuthorizeAnyAttribute
如果上述解决方案有点矫枉过正,始终可以使用手动组合类型创建和注册:
- 创建组合的“有限或完整”策略要求:
public class LimitedOrFullRequirement : ILimitedRequirement, IFullRequirement { }
- 如果这两个要求也必须单独使用(除了使用组合的“有限或完整”策略),请为单个要求创建接口实现:
public class LimitedRequirement : ILimitedRequirement { }
public class FullRequirement : IFullRequirement { }
- 注册政策(请注意,注释掉的政策 完全可选 注册):
services.AddAuthorization(options => {
options.AddPolicy("Limited Or Full",
policy => policy.AddRequirements(new LimitedOrFullRequirement()));
//If these policies also have single use, they need to be registered as well
//options.AddPolicy("Limited",
// policy => policy.AddRequirements(new LimitedRequirement()));
//options.AddPolicy("Full",
// policy => policy.AddRequirements(new FullRequirement()));
});