ASP.NET 5针对两个或多个策略进行授权

pai*_*yff 28 c# authorization asp.net-core-mvc asp.net-core

是否可以针对两个或更多策略应用授权?我使用的是ASP.NET 5,rc1.

[Authorize(Policy = "Limited,Full")]
public class FooBarController : Controller
{
    // This code doesn't work
}
Run Code Online (Sandbox Code Playgroud)

如果没有,我如何在不使用政策的情况下实现这一目标 有两组用户可以访问此控制器:"完整"和"有限".用户可以属于"完整"或"有限",或两者兼而有之.它们只需要属于两个组中的一个以访问该控制器.

blo*_*art 29

不是你想要的方式; 政策旨在累积.例如,如果您使用两个单独的属性,那么它们必须都通过.

您必须在单个策略中评估OR条件.但是您不必在单个处理程序中将其编码为OR.您可以拥有一个具有多个处理程序的需求.如果任一处理程序标记成功,则满足要求.请参阅我的授权工作室中的步骤6 .


And*_*ius 12

设置新策略"LimitedOrFull"(假设它们与声明类型名称匹配)后,创建如下要求:

options.AddPolicy("LimitedOrFull", policy =>
    policy.RequireAssertion(context =>
        context.User.HasClaim(c =>
            (c.Type == "Limited" ||
             c.Type == "Full"))));
Run Code Online (Sandbox Code Playgroud)

https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-2.1#using-a-func-to-fulfill-a-policy


Hom*_*vas 7

Net Core 可以选择拥有多个具有相同 AuthorizationRequirement 类型的 AuthorizationHandler。只有其中之一必须成功通过授权 https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-2.1#why-would-i-want-multiple-需求处理程序

  • options.AddPolicy("ElevatedRights", policy => policy.RequireRole("Administrator", "PowerUser", "BackupAdministrator")); (4认同)

Ada*_*ans 5

使用动态创建的按需需求的解决方案最适合我:

  1. 创建单独的“有限”和“完整”策略要求的接口:
    public interface ILimitedRequirement : IAuthorizationRequirement { }
    public interface IFullRequirement : IAuthorizationRequirement { }
Run Code Online (Sandbox Code Playgroud)
  1. 创建授权的自定义属性:
    [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;
    }
Run Code Online (Sandbox Code Playgroud)
  1. 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);
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)
  1. 如果您的授权处理程序很繁重(例如,其中一个访问数据库),并且您不希望其中一个处理程序在另一个处理程序已成功或失败时进行授权检查,则可以使用下一种解决方法(请记住处理程序注册的顺序)直接决定它们在请求管道中的执行顺序):
    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);
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)
  1. 注册授权处理程序(请注意,没有“LimiterOrFullRequirementHandler”,这两个处理程序将处理组合策略要求):
    //Order of handlers is important - it determines their execution order in request pipeline
    services.AddScoped<IAuthorizationHandler, LimitedRequirementHandler>();
    services.AddScoped<IAuthorizationHandler, FullRequirementHandler>();
Run Code Online (Sandbox Code Playgroud)
  1. 现在我们需要检索所有属性并使用ImpromptuInterface(或用于动态创建类型实例的任何其他工具)AuthorizeAny动态创建它们的需求:
    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);
        }
    }
Run Code Online (Sandbox Code Playgroud)
  1. 注册所有创建的需求
    services.AddAuthorization(options => {
        foreach(KeyValuePair<string, IAuthorizationRequirement> item in requirements) {
             options.AddPolicy(item.Key, x => x.AddRequirements(item.Value));
        }
    }

Run Code Online (Sandbox Code Playgroud)

AuthorizeAttribute如果默认处理方式与自定义相同,则上述解决方案允许处理与 OR 组合相同的单一要求AuthorizeAnyAttribute

如果上述解决方案太过分了,则始终可以使用手动组合类型创建和注册:

  1. 创建组合的“有限或全部”保单要求:
    public class LimitedOrFullRequirement : ILimitedRequirement, IFullRequirement { }
Run Code Online (Sandbox Code Playgroud)
  1. 如果这两个要求也必须单独使用(除了使用组合的“有限或完整”策略之外),请为单个要求创建接口实现:
    public class LimitedRequirement : ILimitedRequirement { }
    public class FullRequirement : IFullRequirement { }
Run Code Online (Sandbox Code Playgroud)
  1. 注册策略(请注意,注释掉的策略完全可以选择注册):
    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()));
            });
Run Code Online (Sandbox Code Playgroud)