在授权期间从身份服务器获取权限

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 为已接受的答案,但我只想为这个问题提供一个替代解决方案。如果您的权限非常简单,例如 readResourcewriteResource,那么您可以将所有权限定义为 enum 并在 JWT 中使用整数而不是字符串,这将减少 JWT 的大小。

如果权限列表仍然很大,那么您还可以将权限分组在一起,这样对于某些客户来说权限列表就更小了,例如将 readResourcewriteResourceupdateResourcedeleteResource 合并到一个名为 crudResource.

的权限中