我不确定如何在 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 条件,我的处理程序就会工作。那么我走在正确的轨道上还是有其他更有意义的方法来实现这一点?
当您想要实现官方文档有一个专用部分。他们提供的解决方案是根据一项要求注册多个授权处理程序。在这种情况下,所有处理程序都会运行,如果至少有一个处理程序成功,则认为满足要求。 不过,我认为该解决方案不适用于您的问题;我可以看到两种很好地实现这一点的方法
在
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)));
复制给那些寻找简短答案的人(注意:下面的解决方案不解决层次结构问题)。 您可以在 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)))));
});
但是,使用声明来实现本质上是基于继承的授权应该对每一层使用不同的声明,即
普通用户应该有claimA
尝试采用分层/继承方法会导致将来出现大问题,就像代码中继承经常出现的情况一样。如果明天有新的要求,管理员和用户应该能够在系统中执行或查看经理无法执行的操作,该怎么办?
使用声明,您可以创建向管理员和用户提供的声明D,但不向经理提供。使用基于继承的解决方案,实现起来要困难得多。