问题描述
我不确定如何在ASP.NET核心授权中实现组合的"OR"要求。在以前的ASP.NET版本中,这将通过角色来完成,但我正在尝试使用声明来完成这一点,部分原因是为了更好地理解它。
用户有一个名为AcCountType的枚举,它将提供对控制器/操作等的不同级别的访问。有三个级别的类型,称为User、BiggerUser和BigsterUser。因此,BigsterUser可以访问他们下面的帐户类型拥有的所有内容,依此类推。我希望通过使用策略的授权标记来实现这一点。
所以首先我有一个要求:
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条件的方法,我的处理程序就会工作。那么,我是在正确的轨道上,还是有其他更有意义的方式来实现这一点?
推荐答案
当您要实现OR逻辑时,官方文档有一个dedicated section。他们提供的解决方案是根据一个需求注册多个授权处理程序。在这种情况下,所有处理程序都会运行,如果至少有一个处理程序成功,则认为满足了要求。
不过,我认为该解决方案不适用于您的问题;我认为有两种方法可以很好地实现这一点
在TypeRequirement
中提供多个AccountTypes
然后,该要求将包含满足该要求的所有值。
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
方法进行比较。我无法快速找到有关此问题的可靠文档,但this code sample on docs.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)));
这篇关于ASP.NET核心授权:组合或要求的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!