在JWT中设置角色是最佳实践吗?

88

我正在考虑使用JWT。在jwt.io的示例中,我看到了以下信息:

"admin": true

管理员可以被视为一个角色,因此我有一个问题。在令牌负载中设置角色是一种惯常/良好的做法吗?考虑到角色可以动态修改,我感到有些疑问。


请记住,如果您修改了用户的角色,则需要发行新的令牌。 - Nico Haase
4个回答

148
官方JWT网站明确提到“授权”(与“认证”相对)作为JWT的用例:
当应该使用JSON Web Tokens? 授权:这是使用JWT的最常见情况。一旦用户登录,每个后续请求都将包括JWT,允许用户使用该令牌允许的路由、服务和资源。单点登录是一种广泛使用JWT的功能,因为其开销小且易于跨不同域使用。
话虽如此,从安全角度来看,您应该三思而后行,是否真的想在令牌中包含角色或权限。
(下面的文本可以理解为更加“深入”的回答)
一旦创建并签署了令牌,您就授予了权限,直到令牌过期。但是如果您出于错误而授予管理员权限呢?在令牌过期之前,某人现在正在使用分配错误的权限在您的站点上操作。
有些人可能会认为令牌的生命周期很短,但考虑到一个人在短时间内可以造成的伤害,这不是一个强有力的论据。另外一些人主张维护一个单独的黑名单数据库表来处理令牌失效的问题,但这会给后端添加某种会话状态跟踪,因为现在你需要跟踪所有当前存在的会话——所以每次请求到达时,你都需要对黑名单进行一次db调用以确保它尚未被列入黑名单。有人可能会认为,这从一开始就打破了“将角色放入JWT中以避免额外的db调用”的目的,因为你只是将额外的“角色db调用”换成了额外的“黑名单db调用”。
因此,您可以将授权声明添加到令牌中,也可以将用户角色和权限信息保存在您的身份验证服务器的数据库中,您随时可以完全控制该数据库(例如,撤销用户的某些权限)。如果收到请求,则从身份验证服务器(或存储权限的任何其他位置)获取当前角色。
顺便说一下,如果您查看IANA注册的公共声明列表,您将看到这些声明围绕身份验证演变,并且没有涉及用户被允许做什么(授权)。
因此,总之您可以...
  • 将角色添加到JWT中,如果(a)方便对您很重要,(b)您想避免额外的数据库调用来获取权限,(c)不在意一个人在某些时间窗口内被分配了他不应该拥有的权限,以及(d)您不关心将权限添加到JWT有效负载大小中导致的轻微增加。

  • 将角色添加到JWT并使用黑名单,如果(a)您想防止任何时间窗口内一个人被分配了他不应该拥有的权限,(b)接受这样做的代价是为每个传入请求请求黑名单,并且(c)您不关心将权限添加到JWT有效负载大小中导致的轻微增加。

  • 不要将角色添加到JWT中,并按需获取它们,如果(a)您想防止任何时间窗口内一个人被分配了他不应该拥有的权限,或者(b)避免黑名单的开销,或者(c)避免将JWT有效负载大小略微增加,以及(d)如果您接受这样做的代价是有时/总是在传入请求上查询角色。


46
如果你错误地授予一个用户管理员权限,而他或她有意愿和能力在短时间内造成重大破坏,那么无论你的授权实施方式如何,你都可能会为自己的错误付出极高的代价。任何网络安全协议都无法防范这种威胁。 - Nate T
1
@NathanToulbert 我完全同意。用户在使用应用程序时犯的错误和不良行为是每个应用程序最薄弱的部分。 - skechav
3
关于第二种方法,想补充一点。使用布隆过滤器创建黑名单可以节省很多对数据库的调用,从而可能降低每个传入请求的成本。 - Kaihua
@NateT 这是一个完全有效的评论,但它也涉及到你团队的人才。当你考虑到性能时,使用基于角色的令牌确实有好处。但我也不会让我的初级开发人员在如此重要的任务上自由发挥。 - Husk Rekoms
1
给出的示例可以改进。如果用户被授予令牌,然后在令牌过期之前该用户被解雇,这可能是提前撤销令牌的更合法原因,而不是人为错误。 - Lionet Chen

86

如果额外的信息对客户端有用,您可以在令牌中创建声明来存储它们。

但是,我建议只使用 JWT 进行身份验证(确定调用者身份)。如果您需要执行授权操作(确定调用者能做什么),请从您的持久存储中查找调用者的角色/权限以获取最新值。

对于短暂的令牌(例如,在微服务集群中传播身份验证和授权时),将角色包含在令牌中也很有用。


24
这样做的缺点是应用程序必须知道这些信息。在许多情况下,除了令牌中声明的内容之外,应用程序无法访问有关用户的其他信息。因此,这取决于确切的用例。但是,向 JWT 添加角色信息是完全正常的。 - pm100
3
虽然“认证(authentication)”和“授权(authorization)”这两个词经常被混用,但它们实际上是不同的概念。谢谢提供这些信息 :) - Ziad Akiki
4
认证基本上是确保用户是其所声称的身份,授权是确保用户有权访问目的地的过程。 - Nate T
现代标准如OIDC和中心身份系统利用JWT声明,实现对访问的完全集中控制。然后系统只需要映射到声明。OIDC的令牌还可以用于生成特定应用程序的令牌。 - Kind Contributor
也许为其他角色单独设置一个独立的Jwt令牌会更好。 - Ernest Rutherford

43

正如这里所提到的,ASP.NET Core将自动检测JWT中提到的任何roles

{
  "iss": "http://www.jerriepelser.com",
  "aud": "blog-readers",
  "sub": "123456",
  "exp": 1499863217,
  "roles": ["Admin", "SuperUser"]
}

并将它们映射到ASP.NET角色中,这通常用于保护应用程序的某些部分。

[Authorize(Roles = "Admin")]
public class SettingsController : Controller

颁发(并签署)JWT的服务器通常被称为授权服务器而不仅仅是身份验证服务器,因此将角色信息(或作用域)包含在JWT中是有意义的,即使它们不是注册声明


当然,你仍然需要以某种方式将它们放在那里;-) 这个问题似乎是实现这一点的一种流行方式 - https://dev59.com/XlgQ5IYBdhLWcg3w6III#42037615 - 特别是如果你在 .NET 领域,并且需要使用 ASPNET 角色或角色声明创建令牌。 - Simon_Weaver
更不用说令牌将作为头部中“authorization”键的值进行传递了。 - Nate T
@ryang 对我仍然有效...你可能想在Stack Apps或Meta Stack Exchange上提交一个错误报告。 - Glorfindel

-2

JWT令牌旨在防篡改。

如果JWT令牌包含一个“角色”声明,指示用户具有某个角色或权限,则客户端无法修改该声明以赋予自己额外的权限。

(如果他这样做了)尝试修改令牌可能会被服务器检测到并拒绝,或者可能只会导致客户端被拒绝访问所请求的资源。

例如,在我的情况下,我正在使用API-Platform,它可以通过验证令牌的签名并根据服务器记录验证其内容来检查修改后的令牌。如果检测到任何修改,服务器将拒绝请求,客户端将无法获得所请求的资源的访问权限。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接