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

      如果权限列表仍然很大,那么您还可以将权限组合在一起,以便某些客户的权限列表更小,例如将readResourcewriteResourceupdateResourcedeleteResource 合并为一个名为crudResource 的权限。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-02-08
        • 2020-11-26
        • 1970-01-01
        • 2015-01-18
        • 1970-01-01
        • 2011-02-19
        • 2019-07-19
        相关资源
        最近更新 更多