首页 文章

处理JWT到期和JWT有效负载更新

提问于
浏览
9

我的个人/爱好应用程序有一个基于Koa的Node.js后端 .

我用JWT令牌实现了会话处理 . 客户端(AngularJS)在成功登录后获取令牌并将令牌存储在某处(当前在 sessionStorage 但是出于此问题的目的,它应该无关紧要) .

我有两个问题:

  • 当我需要更新JWT代表的用户记录时,用户打开了双因素身份验证(2FA),所以我让他提供他的电话号码,我想在用户的记录中设置这个电话号码 . 目前,在成功验证电话号码后,我调用后端更新用户记录,并使用更新的用户记录创建新的JWT令牌(我从JWT令牌中排除敏感信息,如散列密码,但我想要包括客户端使用的电话号码) . 当某些凭据更改并使用此新令牌更新现有客户端令牌时,是否可以创建新令牌?我是否永远不会创建另一个令牌,只创建一个令牌并且只有在成功验证后?然后,我如何更新令牌中的有效负载?

  • 如何处理过期的JWT令牌?在我看来,我有3个(可能的)场景:

2.1 . JWT准备短暂生活,比如说15分钟 . 如果后端服务器回复401 Unauthenticated 'Invalid token'(我想这是 koa-jwt 的默认行为),那么我会自动注销我的客户端并要求重新验证 . 但我还 Build 了一个互补的中间件,它是后端链中的最后一个,用刷新的到期重新创建令牌,客户端也会用刷新的令牌替换现有的令牌 . 因此,如果用户处于活动状态并且每个受保护的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 'invalid token'(默认为 koa-jwt )响应时,客户端将refresh_token发送到后端以要求新的access_token(以及可选的新的refresh_token) . 在这种情况下,我不完全理解如何针对旧的access_token验证refresh_token以提供新令牌?或者为什么我们需要一个refresh_token?

关于上层主题(JWT更新和JWT到期)的任何通用建议都会有所帮助 .

2 回答

  • 6

    从底部开始,我会忽略刷新令牌,因为我认为他们不会在这里帮助你 . 它们通常针对客户端应用程序可以提供比用户浏览器更安全的存储的其他场景 - 考虑本机移动应用程序或服务器端Web应用程序 .

    刷新令牌是长寿的 . 这意味着当客户端从服务器获取一个时,必须安全地存储此令牌以防止其被潜在的攻击者使用,因此将它们存储在浏览器中是不安全的 .

    (重点是我的;来源refresh tokens

    这意味着选项2.3基本上与2.2相同,这不是一个糟糕的选择 . 使用长会话来改善用户体验是可以接受的 . 例如,Django使用其会话cookie年龄的默认值为两周 . 见SESSION_COOKIE_AGE .

    剩下的选项(2.1)通常称为滑动会话 . 会话超时很短,但只要用户在该时间间隔内继续使用该应用程序,会话就会自动更新 . 这可能是最常用的方法,或者至少是我用过的时间,所以我有偏见 . 我唯一要注意的是,滑动会话通常使用不透明的会话标识符实现,客户端存储为cookie,然后存储在服务器上的实际会话数据 .

    您的方法有点不同,因为您有一个存储在浏览器本地存储上的无状态JWT令牌(它包含实际用户数据) . 就像你说的那样,为了更新令牌,你必须生成一个新令牌,因为你必须生成一个新的签名 .

    签名用于验证JWT的发件人是否是其所说的人,并确保邮件未被更改 .

    (重点是我的;来源JSON web tokens

    说了这么多,我会考虑以下几点:

    • 问如果你真的需要JWT,或者如果常规会话标识符存储为cookie(HTTP Only)会简化你的逻辑 .

    • 例如,如果JWT是一个要求,你有另一个API也会接受这些令牌作为身份验证,那么我会考虑选项2.1或2.2作为基于浏览器的应用程序的刷新令牌 .

    话虽如此,你还应该考虑JWT不是很大,但如果你决定自动更新,它们仍然是一个开销 . 您可以通过选择20分钟的会话持续时间来缓解此问题,并且仅在会话结束一半后执行自动续订 .

    另一点是,应用程序中的XSS之类的漏洞会将访问令牌暴露给攻击者,因为注入的脚本可以从 localStorage / sessionStorage 读取,这可能是支持仅HTTP会话cookie存储的另一点 .

  • 3

    在我开始讨论第一个问题之前,我想回答你的第二个问题 .

    基本上,您提到的第三个选项是续订访问令牌的最佳方式 . 访问令牌应该是短暂的生活(~5分钟),刷新令牌的寿命更长 . 当您的访问令牌过期时,将刷新令牌发送到后端并获取新的访问令牌 . 所以你的回答应该是这样的:

    {
    "token_type":"bearer",
    "access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiVlx1MDAxNcKbwoNUwoonbFPCu8KhwrYiLCJpYXQiOjE0NDQyNjI4NjYsImV4cCI6MTQ0NDI2Mjg4Nn0.Dww7TC-d0teDAgsmKHw7bhF2THNichsE6rVJq9xu_2s",
    "expires_in":10,
    "refresh_token":"7fd15938c823cf58e78019bea2af142f9449696b"
    }
    

    因此,我们的想法是将您的应用程序分离到Authorization Server(生成访问令牌/刷新令牌)和Resource Server(验证访问令牌并访问资源) . 您可以维护架构以针对授权服务器中的访问令牌验证刷新令牌 . 请参阅此链接中提到的架构部分,它可能会给您一些想法 . Oauth2 . 您可以根据需要修改架构 . 您无需为每个请求调用发送刷新令牌以及访问令牌 . 刷新令牌只能发送到授权服务器以生成新的访问令牌 . 如何生成刷新令牌?如果我使用Java,我会使用 UUID.randomUUID() 生成一个唯一的刷新令牌 .

    现在回答您的第一个问题,如果您想根据更新的用户记录更新JWT有效负载,则可以使用相同的刷新令牌生成具有更新的有效负载的新访问令牌 . 逻辑保持不变,因为如果用户记录中存在电话号码,则会将其添加到有效负载中,否则,它将在有效负载中为空 .

    使用Refresh令牌的主要优点是可以使用刷新令牌随时更新访问令牌

相关问题