【问题标题】:Fetch permissions from identity server during authorization在授权期间从身份服务器获取权限
【发布时间】:2021-05-16 14:50:35
【问题描述】:
我使用identity server 4进行身份验证和授权,用户权限保存在JWT中,然后在API-s上用于检查用户是否需要权限。
但问题是 JWT 太大了,我想从中删除权限,并在 API-s 上进行自定义授权,以便它从 identity server 获取权限,而不是从 JWT 获取。
API 将仅从 JWT 获取 userId,然后基于此从 identity server 获取附加信息。有可能做这样的事情吗?
【问题讨论】:
标签:
c#
asp.net-core
asp.net-identity
identityserver4
【解决方案1】:
我们的应用程序中基本上也有类似的问题。
解决此问题的方法是在从传入请求中读取并验证 JWT 令牌后,使用在 API 资源(您通过使用 JWT 不记名令牌身份验证保护的 API)级别引发的事件.
此事件称为OnTokenValidated,有关详细信息,请参阅here。
这是顶层计划:
- 尽量减少您的 JWT 不记名令牌。它至少包含主题 ID,这是用户在身份提供者级别的唯一标识符。您可以在那里提出其他声明,但想法是 JWT 不记名令牌必须很小
- 实现一种在给定用户唯一标识符的情况下获取用户权限的方法(您可以使用主题 ID 作为标识符或任何其他在您的系统中有意义的 ID)
- 使上一点的用户权限获取机制可通过api调用访问。缓存这个 API 是个好主意,因为权限通常是稳定的。定义一种清除此缓存的智能方法超出了此答案的范围,但这是您绝对应该考虑的事情。
- 获取用户权限后(通过 API 调用),您需要将其提供给 ASP.NET 核心授权框架。最简单的方法是创建自定义声明类型(例如:“app_permission”)并为每个用户权限创建一个用户声明。这些权限声明中的每一个都具有自定义声明类型(“app_permission”)和权限名称作为声明值。例如,拥有“read-content”和“write-content”两个权限的用户将拥有两个拥有“app_permission”作为声明类型的声明,第一个拥有“read-content”作为声明值,第二个声明将“write-content”作为声明值。
- 可以通过为用户定义一个额外的
ClaimsIdentity 并将其添加到当前用户身份中,将前面定义的权限声明注入用户身份(在 API 资源级别)。此处描述的过程与 MVC 应用程序使用 cookie 身份验证完成的声明转换非常相似。
在您的 API 资源的 Startup 类中,在您注册身份验证服务的位置,您可以执行以下操作:
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://localhost:8080";
options.Audience = "sample-api";
options.RequireHttpsMetadata = false;
// register callbacks for events
options.Events = new JwtBearerEvents
{
OnTokenValidated = context =>
{
if (!context.Principal.Identity.IsAuthenticated)
{
return;
}
var subjectId = context.Principal.FindFirst(JwtClaimTypes.Subject)?.Value;
if (string.IsNullOrWhiteSpace(subjectId))
{
return;
}
// do whatever you want with the user subjectId in order to get user permissions.
//You can resolve services by using context.HttpContext.RequestServices which is an instance of IServiceProvider
//Usually you will perform an API call to fetch user permissions by using the subject id as the user unique identifier
// User permissions are usually transformed in additional user claims, so that they are accessible from ASP.NET core authorization handlers
var identity = new ClaimsIdentity(userPermissionsClaims);
context.Principal.AddIdentity(identity);
}
};
});
【解决方案2】:
+1 表示已接受的答案,但我只想为这个问题提供一个替代解决方案。如果您的权限非常简单,例如 readResource 或 writeResource,那么您可以将所有权限定义为 enum,并在 JWT 中使用整数而不是字符串,这将减少 JWT 的大小。
如果权限列表仍然很大,那么您还可以将权限组合在一起,以便某些客户的权限列表更小,例如将readResource、writeResource、updateResource、deleteResource 合并为一个名为crudResource 的权限。