在授权期间从身份服务器获取权限
Fetch permissions from identity server during authorization
我正在使用identity server 4
进行身份验证和授权,用户权限保存在JWT
中,然后在API-s上使用来检查用户是否需要权限。
但问题是 JWT
变得太大了,我想删除它的权限,并在 API-s 上进行自定义授权,以便它从 [=13= 获取权限] 而不是从 JWT 获取。
API 只会从 JWT
获得 userId
,然后基于此从 identity server
获取额外信息。是否可以这样做?
我们的应用基本上也有类似的问题。
解决此问题的方法是使用在 API 资源级别引发的事件(您使用 JWT 不记名令牌身份验证保护的 API)一旦 JWT已从传入请求中读取并验证令牌。
此事件称为 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
class 中,在您注册身份验证服务的地方,您可以这样做:
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);
}
};
});
+1 为已接受的答案,但我只想为这个问题提供一个替代解决方案。如果您的权限非常简单,例如 readResource
或 writeResource
,那么您可以将所有权限定义为 enum
并在 JWT 中使用整数而不是字符串,这将减少 JWT 的大小。
如果权限列表仍然很大,那么您还可以将权限分组在一起,这样对于某些客户来说权限列表就更小了,例如将 readResource
、writeResource
、updateResource
、deleteResource
合并到一个名为 crudResource
.
的权限中
我正在使用identity server 4
进行身份验证和授权,用户权限保存在JWT
中,然后在API-s上使用来检查用户是否需要权限。
但问题是 JWT
变得太大了,我想删除它的权限,并在 API-s 上进行自定义授权,以便它从 [=13= 获取权限] 而不是从 JWT 获取。
API 只会从 JWT
获得 userId
,然后基于此从 identity server
获取额外信息。是否可以这样做?
我们的应用基本上也有类似的问题。
解决此问题的方法是使用在 API 资源级别引发的事件(您使用 JWT 不记名令牌身份验证保护的 API)一旦 JWT已从传入请求中读取并验证令牌。
此事件称为 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
class 中,在您注册身份验证服务的地方,您可以这样做:
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);
}
};
});
+1 为已接受的答案,但我只想为这个问题提供一个替代解决方案。如果您的权限非常简单,例如 readResource
或 writeResource
,那么您可以将所有权限定义为 enum
并在 JWT 中使用整数而不是字符串,这将减少 JWT 的大小。
如果权限列表仍然很大,那么您还可以将权限分组在一起,这样对于某些客户来说权限列表就更小了,例如将 readResource
、writeResource
、updateResource
、deleteResource
合并到一个名为 crudResource
.