JWT 是明文签名方案,也就是你在 JWT 中存储的所有东西都是明文(base64 编码)的,加上 secret 使用 HMAC 算法获取摘要签名,通过签名来检查这个 JWT 是否被篡改。
所以 JWT 通常是一次签发,永久有效的。
为了避免这个问题,通常会在 JWT 的数据中放入一个过期时间,虽说是明文放进去的,但是受签名限制,无法篡改。这样,服务端在检查 JWT 是否被篡改的同时检查过期时间,使得这个 JWT 不会永久有效。
但是还有一个问题就是,JWT 在过期时间到达之前,依旧是一直有效的,但这个问题就类似于:你的密码在修改之前是一直有效的。所以“只要拿到 JWT 就可以冒充用户做任何事情了”和“只要拿到用户的密码就可以冒充用户做任何事情了”是一样的……
所以在防止冒充这个部分,你需要其他的防护手段,比如 HTTPS 双向认证,双向加密之类的。
JWT 相比密码,区别在于,JWT 是临时生成的,在有效期内总是有效,超过有效期需要重新生成;而密码是用户设置的,在用户主动修改之前总是有效的,有效期由其他策略控制,但通常都很长,用户修改密码后旧密码立即失效。
所以,如果使用 JWT 做 session 保持,那么在用户修改密码之后,JWT 不会过期,所有持有 JWT 的人依旧可以继续访问。因为 JWT 本身做不到主动失效,相当于 JWT 是用户的一个不受用户控制的“密码”。
如果你的系统支持 JWT 续期,那么一旦 JWT 泄漏,用户将无法找回账号,因为第三方可以拿着截获的 JWT 进行无限续期,用户改密码都不行的。
解决办法就是,在用户退出,或是修改密码的时候,使此用户的所有 JWT 立即失效。前面说过,JWT 一旦生成,在有效期之前总是有效的,是不受控制的……
所以为了使 JWT 立即过期,服务端必须记录所有生成的有效的 JWT,在使用 secret 校验的时候检查 JWT 是否在有效 JWT 列表中,如果不在,那么即便是 JWT 有效也拒绝认证。
这样,只要在用户退出登录,或是修改密码的时候,删除服务端此用户的所有 JWT 记录即可。
但是,到了这个模式,如果服务端要支持用户退出,则必须要记录 JWT 的有效性,而记录 JWT 有效性,不如直接记录一个安全随机数……
也就是说,JWT 本身并不是用来做 session 保持的,session 保持还是用服务端生成的一段安全随机数来进行比较好。
而 JWT 的使用场景,通常是在服务切换的时候,用作身份令牌的。比如从 A 服务跳转到 B 服务,要带过去一些信息,这时候就可以用 JWT 来实现了,JWT 的有效期通常设置为不超过 5 秒。