JWT 的一些思考

sorcererxw /

简介

Json Web Token(简称JWT)是一鉴权方式,不同传统的Session模式,JWT不需要服务端保存用户登录状态,即无状态,而是选择将状态使用非对称加密保存在Token里面,用户可以使用这个token进行访问各类资源。

这个无状态鉴权方式随着微服务的流行,也被越来越多地使用,这样子可以让后台的各类资源服务与鉴权服务进行解耦,资源服务器在拿到的数据是一件被网关或者中间件解析完毕的用户信息,根据这个信息进行对应操作就可以了。

思考

为什么要使用 AccessToken+RefreshToken 的组合?

单单使用AccessToken自然也能够实现鉴权+刷新,大致的做法就是在AccessToken里面包含access_expire_timeoutrefresh_expire_timeout两个时间,来标示这个token所能够进行的操作。但是这个做个存在安全性问题,就像一个token既是运动员也是裁判员一样,既能够访问resource也能够刷新,一旦这个token被泄露,那么很难对这个token做出限制。

而使用将访问资源刷新token两种权限分开在AccessToken和RefreshToken,进行权限隔离,一定程度上保证安全性。

  • 假如AccessToken泄露了,由于AccessToken较短的过期时间(几十分钟),也不会存在太大影响。
  • 假如RefreshToken泄露了,由于Refresh的时候需要带上设备信息,第三方也很难非法使用。

AccessToken和RefreshToken应该包含哪些信息?

RefreshToken 和 AccessToken 一样,都是一个JWT,但是过期时间和包含的信息量不同。

RefreshToken只需要包含基础的用户ID+设备ID,保证的refresh的时候能够定位到对应设备即可

相应的,RefreshToken也可以只存放这些信息,但是为了防止不必要的请求其他服务,可以存放更多冗余信息

AccessToken 和 RefreshToken 是否需要使用不同 KeyPair?

如果你的 AccessToken 和 RefreshToken 可以使用同一套 key 进行解密,恰好 AccessToken 和 RefreshToken 也都包含相似的信息,那么用户完全可以使用 RefreshToken 作为 AccessToken 来访问,而且不用担心过期的问题。这会产生不小的安全问题。

解决方案就是使用不用 key 进行加解密,保证两个 token 不能互相替换。

如何对一组Token进行拉黑?

使用 JWT 的一个目的就是不需要在服务端进行保存用户的 token,所以如果想拉黑一个用户或者一个设备,也无从得知这个用户或者设备对应的 token。当然,也可以在建立一个全局的用户黑名单,在每次访问资源的时候进行检查,但是这就违背了资源服务器不需要操作用户鉴权这个设计原则了。

当然也可以在RefreshToken的角度进行处理。因为Refresh操作不会频繁进行,但是由于AccessToken过期时间比较短,那么Refresh操作每隔较短的时间都会进行一次,如果在Refresh的时候,根据其中的用户信息和设备信息,进行鉴定从而决定能否Refresh。这样子也不会对资源服务器造成任何负担。

如何实现零延迟地强制下线?

如上面所说的拉黑方式,实现下线的最长延迟是AccessToken的过期时间。如果涉及到敏感信息,希望立即将危险用户或者设备下线,就很难做到。

通过在App冷启动或者网页打开时候进行Refresh,可以尽可能减少危害。但是依然无法很好的地满足需求。

我们平时看到Google、Telegram之类的安全中心,能够看到一个所有设备列表,可以选择让这个设备立即退出登录。假设这个产品也是使用了OAuth,那么这又是如何实现的呢?

猜测操作流程:

  • 将对应设备放入一个黑名单
  • 如果设备在线,通过通知推送的方式,要求其进行refresh或者直接下线。(不过这就又是另一个话题了)
  • 如果设备不在线,那么在下一次启动的时候执行refresh,也能够实现下线。