我的个人/爱好应用程序有一个基于Koa的Node.js后端。
我使用JWT令牌实现了会话处理。客户端(AngularJS)在成功登录后获取令牌并将令牌存储在某处(当前位于sessionStorage
但是出于此问题的目的,这并不重要)。
我有两个问题:
当我需要更新JWT代表的用户记录时,用户打开双因素身份验证(2FA),所以我让他提供他的电话号码,我想设置这个电话号码在用户的记录中。目前,在对电话号码进行成功验证后,我调用后端更新用户记录,并使用更新的用户记录创建新的JWT令牌(我从JWT令牌中排除敏感信息,如散列密码,但我' d喜欢包括客户端使用的电话号码)。当某些凭据更改并使用此新令牌更新现有客户端令牌时,是否可以创建新令牌?我是否永远不会创建另一个令牌,只创建一个令牌并且只有在成功验证后?如何更新令牌中的有效负载?
我应该如何处理过期的JWT令牌?在我看来,我有3个(可能的)场景:
2.1。 JWT准备短暂生活,比如说15分钟。如果后端服务器回复 401 Unauthenticated ' Invalid token' (我想这是koa-jwt
的默认行为)然后我自动注销我的客户端并要求重新验证。但我还建立了一个互补的中间件,它是后端链中的最后一个,用刷新的到期重新创建令牌,客户端也会用刷新的令牌替换现有的令牌。因此,如果用户处于活动状态并且每次受保护的API调用都使用该应用程序,则在成功的情况下,将创建一个新令牌来替换旧令牌。
2.2。 JWT设置为长寿,比如1周,如果它到期,我选择从客户端重新进行身份验证。
2.3。复制https://tools.ietf.org/html/rfc6749#section-1.5。在成功验证后创建JWT令牌时,我们发送access_token和refresh_token。当access_token过期且服务器使用 HTTP 401 '无效令牌回复时#39; (koa-jwt
默认值)然后客户端将refresh_token发送到后端以要求新的access_token(以及可选的新的refresh_token)。在这种情况下,我不完全理解如何针对旧的access_token验证refresh_token以提供新令牌?或者为什么我们需要一个refresh_token?
关于上层主题(JWT更新和JWT到期)的任何通用建议都会有所帮助。
答案 0 :(得分:6)
从底部开始,我会忽略刷新令牌,因为我认为他们不会帮助你。它们通常针对其他场景,客户端应用程序可以提供比用户浏览器更安全的存储 - 想想本机移动应用程序或服务器端Web应用程序。
刷新令牌是长寿。这意味着当客户端从服务器获取一个时,必须安全地存储此令牌以防止其被潜在的攻击者使用,因此将它们存储在浏览器中是不安全的。
(重点是我的;来源refresh tokens)
这意味着选项2.3与2.2基本相同,这不是一个糟糕的选择。拥有长会话持续时间的Web应用程序并不罕见。如果您的应用程序不是非常敏感,那么使用长会话来改善用户体验是可以接受的。例如,Django使用其会话cookie年龄的默认值为两周。请参阅SESSION_COOKIE_AGE。
其余选项(2.1)通常称为滑动会话。会话超时很短,但只要用户在该时间间隔内继续使用该应用程序,会话就会自动更新。这可能是最常用的方法,或者至少是我用过的时间,所以我有偏见。我唯一要注意的是,滑动会话通常使用不透明的会话标识符实现,客户端存储为cookie,然后存储在服务器上的实际会话数据。
您的方法有点不同,因为您有一个存储在浏览器本地存储上的无状态JWT令牌(它包含实际用户数据)。就像你说的,为了更新令牌,你必须生成一个新令牌,因为你必须生成一个新的签名。
签名用于验证JWT的发件人是否为的人,并确保邮件未被更改。
(重点是我的;来源JSON web tokens)
说了这么多,我会考虑以下几点:
话虽如此,您还应该考虑JWT并不大,但如果您决定自动续订,它们仍将是一个开销。您可以通过选择20分钟的会话持续时间来缓解此问题,并且仅在会话结束一半后执行自动续订。
另一点是,应用程序中的XSS之类的漏洞会将访问令牌暴露给攻击者,因为注入的脚本可以从localStorage
/ sessionStorage
读取,这可能是另一个问题。支持仅HTTP会话cookie存储。
答案 1 :(得分:3)
我想在回到第一个问题之前回答你的第二个问题。
基本上,您提到的第三个选项是续订访问令牌的最佳方式。访问令牌应该是短暂的生活(~5分钟),刷新令牌的寿命更长。当您的访问令牌过期时,将刷新令牌发送到后端并获取新的访问令牌。所以你的回答应该是这样的:
{
"token_type":"bearer",
"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiVlx1MDAxNcKbwoNUwoonbFPCu8KhwrYiLCJpYXQiOjE0NDQyNjI4NjYsImV4cCI6MTQ0NDI2Mjg4Nn0.Dww7TC-d0teDAgsmKHw7bhF2THNichsE6rVJq9xu_2s",
"expires_in":10,
"refresh_token":"7fd15938c823cf58e78019bea2af142f9449696b"
}
因此,我们的想法是将您的应用程序分离到Authorization Server(生成访问令牌/刷新令牌)&资源服务器(验证访问令牌并访问资源)。您可以维护架构以针对授权服务器中的访问令牌验证刷新令牌。请参阅此链接中提到的架构部分,它可能会给您一些想法。 Oauth2。您可以根据需要修改架构。您无需为每个请求调用发送刷新令牌以及访问令牌。刷新令牌只能发送到授权服务器以生成新的访问令牌。如何生成刷新令牌?如果我使用的是Java,我会使用UUID.randomUUID()
生成一个唯一的刷新令牌。
现在回答您的第一个问题,如果您想根据更新的用户记录更新JWT有效负载,则可以使用相同的刷新令牌生成具有更新的有效负载的新访问令牌。逻辑保持不变,因为如果用户记录中存在电话号码,则会将其添加到有效负载中,否则,它将在有效负载中为空。
使用Refresh令牌的主要优点是可以使用刷新令牌随时续订访问令牌