Role/User 在 ASP.Net.Core.Identity 中可以拥有多少个声明?

How many claims can a Role/User have in ASP.Net.Core.Identity?

一个角色或用户可以拥有多少个声明?我一直在使用 ASP.Net.Core 2.2 和 AspNet.Core.Identity 开发应用程序。在我的浏览器上测试之前一切正常。在VS2019调试下没有这个问题

我部署了我的应用程序以进行进一步测试,并 运行 出现此错误(如下)。我在 IE, GC 和 FF 中也有同样的问题。

HTTP Error 400. The size of the request headers is too long.

我正在使用 RolesRoleClaims

经过一番挖掘,我发现它与 Role/Claims 有关,事实上它们太多了,它会破坏缓存。基本上 Identity 试图将所有声明存储在一个 cookie 中,但现在 cookie 太大了。

微软给你所有的复杂性只是为了让你被浏览器挫败,这似乎真的很奇怪。

所以我的问题是: - 如果由于浏览器限制而无法利用它们,roles/claims 有什么意义? - 是否有任何关于假想限制(每个角色的最大索赔数)的记录?

限制不是虚的,也不是browser-based。请求限制由服务器设置,是一种安全措施。因此,尝试增加限制确实不是一个好主意。

此外,这里没有关于索赔的最大数量之类的实际指导,因为它基于变量:个人索赔的类型和大小,以及请求的其他部分,例如附加 headers 可以在任何给定时间添加也可以不添加。但是,限制通常是 8-32K,即使在 low-end 上,在达到限制之前您仍然应该能够添加很多声明。一般来说,您还应该考虑传输大小。声明是身份验证票的一部分,这意味着客户端必须在每个请求中传输它们。每个请求 8K 可能看起来不是很多,但在用户 session 的生命周期内会显着增加,尤其是当用户的数据连接速度慢或受限时。

总而言之,您在这里应该做的最好的事情就是减少索赔金额。并非所有事情都需要或应该是声明。将附加数据存储在用户配置文件中并在必要时进行查询。作为声明,您绝对应该拥有的唯一东西是关键的东西,例如 id、username/email 和角色。如果需要或需要时,其他任何东西都可以在以后从备用商店获取。

首先,还有一个选项可以使用备用存储来实际保存授权票据数据。无需将所有内容作为 cookie 发送,然后必须将其与每个请求一起发回,您可以简单地发送一个标识符,然后根据它提取实际数据,类似于 sessions 的工作方式。这是通过创建 ITicketStore 的实现并将其设置为 CookieAuthenticationOptions.SessionStore. There's an old sample for using cache as the store 来完成的。您可能需要修改 ASP.NET Core 新版本的代码。

只是想让你知道,无论我尝试什么,我都无法让 ITicketStore 工作。我什至花了几天时间阅读我能在 ITicketStore 上找到的所有文档,它根本没有很好的记录。这似乎是一个很常见的问题(cookie 和 ITicketStore 文档)。

无论如何,我更改了我的应用程序,以便 Asp.Net.Identity 只选择用户角色。这解决了 cookie 大小问题。

我已经实现了自己的功能来检查角色声明。我觉得哪个效果更好,因为任何更改都是实时发生的,因此无需登录 on/off 即可获得新声明或撤销现有声明。

我有一个服务函数,需要一个 IdentityUser<AppUser> 和一个声明名称(字符串)。

_adminService.UserHasClaim(user, ClaimName) // returns true or false if you have that claim or not

我有自己的 table,与包含值的 AspNetRoleClaims table 相同。 table 的 Asp.Net.Identity 版本现在是空的,因此 cookie 不包含任何声明。

    public bool UserHasClaim(AppUser user, string claimValue)
    {
        var claim = (from c in _context.AppUserRoleClaims
                     join t in _context.AppUserClaimTypes on c.ClaimID equals t.ID
                     join r in _context.UserRoles on c.RoleId equals r.RoleId
                     where r.UserId == user.Id && t.ClaimValue == claimValue select c).FirstOrDefault();

        return (claim != null);
    }

它可能不完美或不理想,但它确实有效,我再也没有时间尝试使用身份来解决这个问题。我并不认为它是理想的,我可能会稍后再回来讨论它。

很高兴就更好的可行解决方案提出意见,但现在这就是我解决问题的方式。

所以,继续...