ASP.NET Core 授权:组合 OR 需求

问题描述 投票:0回答:3

我不确定如何在 ASP.NET Core 授权中实现组合的“OR”要求。在以前版本的 ASP.NET 中,这可以通过角色来完成,但我尝试通过声明来完成此操作,部分原因是为了更好地理解它。

用户有一个名为 AccountType 的枚举,它将提供对控制器/操作等的不同级别的访问。类型分为三个级别,分别称为 User、BiggerUser 和 BiggestUser。因此 BiggestUser 可以访问其下面的帐户类型所拥有的所有内容,依此类推。我想通过使用策略的授权标签来实现这一点。

所以首先我有一个要求:

public class TypeRequirement : IAuthorizationRequirement
{
    public TypeRequirement(AccountTypes account)
    {
        Account = account;
    }

    public AccountTypes Account { get; }
}

我创建政策:

services.AddAuthorization(options =>
{
    options.AddPolicy("UserRights", policy => 
        policy.AddRequirements(new TypeRequirement(AccountTypes.User));
});

通用处理程序:

public class TypeHandler : AuthorizationHandler<TypeRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TypeRequirement requirement)
    {
        if (!context.User.HasClaim(c => c.Type == "AccountTypes"))
        { 
            context.Fail();
        }

        string claimValue = context.User.FindFirst(c => c.Type == "AccountTypes").Value;
        AccountTypes claimAsType = (AccountTypes)Enum.Parse(typeof(AccountTypes), claimValue);
        if (claimAsType == requirement.Account)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

我要做的就是在政策中添加多个要求,其中“任何”都可以满足它。但我目前的理解是,如果我做类似的事情: options.AddPolicy("UserRights", policy => policy.AddRequirements( new TypeRequirement(AccountTypes.User), new TypeRequirement(AccountTypes.BiggerUser) );

两个

要求都必须得到满足。如果 AddRequirements 中有某种方式指定 OR 条件,我的处理程序就会工作。那么我走在正确的轨道上还是有其他更有意义的方法来实现这一点?

当您想要实现
asp.net-core-2.0
3个回答
11
投票
逻辑时,

官方文档有一个专用部分。他们提供的解决方案是根据一项要求注册多个授权处理程序。在这种情况下,所有处理程序都会运行,如果至少有一个处理程序成功,则认为满足要求。 不过,我认为该解决方案不适用于您的问题;我可以看到两种很好地实现这一点的方法


AccountTypes

中提供多个
TypeRequirement

该要求将保存满足要求的所有值。

public class TypeRequirement : IAuthorizationRequirement { public TypeRequirement(params AccountTypes[] accounts) { Accounts = accounts; } public AccountTypes[] Accounts { get; } }

处理程序然后验证当前用户是否与定义的帐户类型之一匹配

public class TypeHandler : AuthorizationHandler<TypeRequirement> { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TypeRequirement requirement) { if (!context.User.HasClaim(c => c.Type == "AccountTypes")) { context.Fail(); return Task.CompletedTask; } string claimValue = context.User.FindFirst(c => c.Type == "AccountTypes").Value; AccountTypes claimAsType = (AccountTypes)Enum.Parse(typeof(AccountTypes),claimValue); if (requirement.Accounts.Any(x => x == claimAsType)) { context.Succeed(requirement); } return Task.CompletedTask; } }

这允许您创建多个使用相同要求的策略,除非您需要为每个策略定义 
AccountTypes

的有效值


options.AddPolicy( "UserRights", policy => policy.AddRequirements(new TypeRequirement(AccountTypes.User, AccountTypes.BiggerUser, AccountTypes.BiggestUser))); options.AddPolicy( "BiggerUserRights", policy => policy.AddRequirements(new TypeRequirement(AccountTypes.BiggerUser, AccountTypes.BiggestUser))); options.AddPolicy( "BiggestUserRights", policy => policy.AddRequirements(new TypeRequirement(AccountTypes.BiggestUser)));


使用枚举比较功能

正如您在问题中所说,您对待

AccountTypes

不同值的方式存在层次结构


    User
  • 可以访问一些东西;
  • BiggerUser
  • 可以访问
    User
    可以访问的所有内容,以及其他一些内容;
  • BiggestUser
  • 可以访问所有内容
    
    
    
  • 这个想法是,要求定义需要满足的
AccountTypes

最低 值,然后处理程序会将其与用户的帐户类型进行比较。


枚举可以与

<=

>=
运算符进行比较,也可以使用
CompareTo
方法。我无法快速找到这方面的可靠文档,但
learn.microsoft.com 上的此代码示例
显示了小于或等于运算符的用法。 要利用此功能,枚举值需要与您期望的层次结构相匹配,例如:

public enum AccountTypes { User = 1, BiggerUser = 2, BiggestUser = 3 }

public enum AccountTypes { User = 1, BiggerUser, // Automatiaclly set to 2 (value of previous one + 1) BiggestUser // Automatically set to 3 }

需求代码、处理程序和策略声明将如下所示:

public class TypeHandler : AuthorizationHandler<TypeRequirement> { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TypeRequirement requirement) { if (!context.User.HasClaim(c => c.Type == "AccountTypes")) { context.Fail(); return Task.CompletedTask; } string claimValue = context.User.FindFirst(c => c.Type == "AccountTypes").Value; AccountTypes claimAsType = (AccountTypes)Enum.Parse(typeof(AccountTypes),claimValue); if (claimAsType >= requirement.MinimumAccount) { context.Succeed(requirement); } return Task.CompletedTask; } }

options.AddPolicy(
    "UserRights",
    policy => policy.AddRequirements(new TypeRequirement(AccountTypes.User)));

options.AddPolicy(
    "BiggerUserRights",
    policy => policy.AddRequirements(new TypeRequirement(AccountTypes.BiggerUser)));

options.AddPolicy(
    "BiggestUserRights",
    policy => policy.AddRequirements(new TypeRequirement(AccountTypes.BiggestUser)));


5
投票
原始答案

复制给那些寻找简短答案的人(注意:下面的解决方案不解决层次结构问题)。 您可以在 Startup.cs 中添加

OR

条件: 例如。我希望只有“John Doe”、“Jane Doe”用户才能查看“结束合同”屏幕,或者仅来自“MIS”部门的任何人也能够访问同一屏幕。下面的内容对我有用,我有声明类型“部门”和“用户名”:

services.AddAuthorization(options => { options.AddPolicy("EndingContracts", policy => policy.RequireAssertion(context => context.User.HasClaim(c => (c.Type == "department" && c.Value == "MIS" || c.Type == "UserName" && "John Doe, Jane Doe".Contains(c.Value))))); });



0
投票

但是,使用声明来实现本质上是基于继承的授权应该对每一层使用不同的声明,即

普通用户应该有claimA
  • 经理应该拥有claimB和claimA,以便能够执行普通用户可以执行的所有操作+经理自己的权限
  • 管理员应该拥有claimC、claimB和claimA,以便能够执行只有管理员可以执行的操作+经理和普通用户可以执行的所有操作
  • 层次划分清晰、易于扩展、灵活。

尝试采用分层/继承方法会导致将来出现大问题,就像代码中继承经常出现的情况一样。如果明天有新的要求,管理员和用户应该能够在系统中执行或查看经理无法执行的操作,该怎么办?

使用声明,您可以创建向管理员和用户提供的声明D,但不向经理提供。使用基于继承的解决方案,实现起来要困难得多。

© www.soinside.com 2019 - 2024. All rights reserved.