ASP.NET 核心身份中的分层政策/要求
Hierarchical policies / requirements in ASP.NET Core Identity
我刚刚开始使用 ASP.NET Core Identity 并定义了以下要求:
public sealed class IsCustomerUserRequirement : IAuthorizationRequirement
public sealed class IsSuperUserRequirement : IAuthorizationRequirement
使用以下基本处理程序:
public class IsCustomerUserHandler : AuthorizationHandler<IsCustomerUserRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, IsCustomerUserRequirement requirement)
{
if (context.User.HasClaim(_ => _.Type == "customer"))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
public class IsSuperUserHandler : AuthorizationHandler<IsSuperUserRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, IsSuperUserRequirement requirement)
{
if (context.User.IsInRole("super_user"))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
然后我可以将这些放在基本政策中:
services
.AddAuthorization(options =>
{
options.AddPolicy("MustBeSuperUser", policy => policy.Requirements.Add(new IsSuperUserRequirement()));
options.AddPolicy("CustomersOnly", policy => policy.Requirements.Add(new IsCustomerUserRequirement()));
});
并使用 [Authorize("CustomersOnly")]
应用它,效果很好。
我的要求 是能够允许超级用户,声明具有 super_user
角色但 没有 customer
声明,也可以访问仅限客户的区域。
我目前通过将处理程序更改为手动检查来实现此功能:
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, IsCustomerUserRequirement requirement)
{
if (context.User.HasClaim(_ => _.Type == Claims.Customer) ||
context.User.IsInRole(Roles.SuperUser))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
我的问题 感觉我没抓住重点。有没有更好的方法来定义它,这样我以后就不必在每个处理程序中重复超级用户检查了?
所有这一切的大局是我使用IdentityServer4(ASP.NET Identity-backed)进行身份验证,然后打算使用一些基于JWT的声明(一个声明,两个角色)来进一步识别用户授权落入应用程序-特定的角色/权限结构和一些与 Identity Server 无关的自定义中间件。围绕该主题有哪些最佳实践(如果有)?
“这感觉好像我没抓住要点” – 是的,在某种程度上你没抓住要点。您正在进行基于角色的授权:用户可以是客户或超级用户。
但是,新模型是基于声明的授权,其中用户对某事有声明,而您正在使用它来授权他们。因此,理想情况下,超级用户将获得与客户相同的声明,并被允许以这种方式访问资源。这样的声明也不会被称为 customer
,而是用户的 属性。
您仍然可以使用带有声明的基于角色的授权模型,但您应该避免混合它们。正如您自己注意到的那样,这最终会变得有点奇怪。
话虽这么说,有多种方法可以使用不同的要求使政策成功。如果您只使用角色(而不是 customer
声明),您可以简单地使用内置方式:
options.AddPolicy("MustBeSuperUser", policy => policy.RequireRole("super_user"));
options.AddPolicy("CustomersOnly", policy => policy.RequireRole("customer", "super_user"));
这样,CustomersOnly
策略将由 customer
和 super_user
角色执行。
由于您没有为客户使用角色,因此您必须在此处遵循您的要求实施。授权要求的工作方式是,对于同一要求类型,您可以有多个处理程序,并且只有其中一个需要成功(只要 none 失败 )即可满足要求成功。
因此您可以让 IsSuperUserHandler
处理多个需求。您可以按照 AuthorizationHandler<T>
实施来完成这项工作:
public class IsSuperUserHandler : IAuthorizationHandler
{
public virtual async Task HandleAsync(AuthorizationHandlerContext context)
{
foreach (var req in context.Requirements)
{
if (req is IsSuperUserRequirement || req is IsCustomerUserRequirement)
{
if (context.User.IsInRole("super_user"))
context.Succeed(req);
}
}
}
}
因此您的 IsSuperUserHandler
现在是 IsSuperUserRequirement
和 IsCustomerUserRequirement
的授权处理程序。因此,需要 IsCustomerUserRequirement
的 CustomersOnly
政策也将为超级用户实现。
我刚刚开始使用 ASP.NET Core Identity 并定义了以下要求:
public sealed class IsCustomerUserRequirement : IAuthorizationRequirement
public sealed class IsSuperUserRequirement : IAuthorizationRequirement
使用以下基本处理程序:
public class IsCustomerUserHandler : AuthorizationHandler<IsCustomerUserRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, IsCustomerUserRequirement requirement)
{
if (context.User.HasClaim(_ => _.Type == "customer"))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
public class IsSuperUserHandler : AuthorizationHandler<IsSuperUserRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, IsSuperUserRequirement requirement)
{
if (context.User.IsInRole("super_user"))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
然后我可以将这些放在基本政策中:
services
.AddAuthorization(options =>
{
options.AddPolicy("MustBeSuperUser", policy => policy.Requirements.Add(new IsSuperUserRequirement()));
options.AddPolicy("CustomersOnly", policy => policy.Requirements.Add(new IsCustomerUserRequirement()));
});
并使用 [Authorize("CustomersOnly")]
应用它,效果很好。
我的要求 是能够允许超级用户,声明具有 super_user
角色但 没有 customer
声明,也可以访问仅限客户的区域。
我目前通过将处理程序更改为手动检查来实现此功能:
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, IsCustomerUserRequirement requirement)
{
if (context.User.HasClaim(_ => _.Type == Claims.Customer) ||
context.User.IsInRole(Roles.SuperUser))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
我的问题 感觉我没抓住重点。有没有更好的方法来定义它,这样我以后就不必在每个处理程序中重复超级用户检查了?
所有这一切的大局是我使用IdentityServer4(ASP.NET Identity-backed)进行身份验证,然后打算使用一些基于JWT的声明(一个声明,两个角色)来进一步识别用户授权落入应用程序-特定的角色/权限结构和一些与 Identity Server 无关的自定义中间件。围绕该主题有哪些最佳实践(如果有)?
“这感觉好像我没抓住要点” – 是的,在某种程度上你没抓住要点。您正在进行基于角色的授权:用户可以是客户或超级用户。
但是,新模型是基于声明的授权,其中用户对某事有声明,而您正在使用它来授权他们。因此,理想情况下,超级用户将获得与客户相同的声明,并被允许以这种方式访问资源。这样的声明也不会被称为 customer
,而是用户的 属性。
您仍然可以使用带有声明的基于角色的授权模型,但您应该避免混合它们。正如您自己注意到的那样,这最终会变得有点奇怪。
话虽这么说,有多种方法可以使用不同的要求使政策成功。如果您只使用角色(而不是 customer
声明),您可以简单地使用内置方式:
options.AddPolicy("MustBeSuperUser", policy => policy.RequireRole("super_user"));
options.AddPolicy("CustomersOnly", policy => policy.RequireRole("customer", "super_user"));
这样,CustomersOnly
策略将由 customer
和 super_user
角色执行。
由于您没有为客户使用角色,因此您必须在此处遵循您的要求实施。授权要求的工作方式是,对于同一要求类型,您可以有多个处理程序,并且只有其中一个需要成功(只要 none 失败 )即可满足要求成功。
因此您可以让 IsSuperUserHandler
处理多个需求。您可以按照 AuthorizationHandler<T>
实施来完成这项工作:
public class IsSuperUserHandler : IAuthorizationHandler
{
public virtual async Task HandleAsync(AuthorizationHandlerContext context)
{
foreach (var req in context.Requirements)
{
if (req is IsSuperUserRequirement || req is IsCustomerUserRequirement)
{
if (context.User.IsInRole("super_user"))
context.Succeed(req);
}
}
}
}
因此您的 IsSuperUserHandler
现在是 IsSuperUserRequirement
和 IsCustomerUserRequirement
的授权处理程序。因此,需要 IsCustomerUserRequirement
的 CustomersOnly
政策也将为超级用户实现。