知识回顾
众所周知,在 OAuth2 体系中认证通过后返回的令牌信息分为两大类:不透明令牌(opaque tokens) 和 透明令牌(not opaque tokens)。
不透明令牌 是一种无可读性的令牌,一般来说就是一段普通的 UUID 字符串。使用不透明令牌时资源服务不知道这个令牌是什么,代表谁,需要调用认证服务器校验、获取用户信息。使用不透明令牌采用的是 中心化 的架构。
透明令牌 一般指的是我们常说的JWT Token,用户信息保存在 JWT 字符串中,资源服务器自己可以解析令牌不再需要去认证服务器校验令牌。使用JWT是属于 无状态、去中心化 的架构。
一旦我们选择了使用JWT,就需要明确一点:在不借助外力的情况下,让JWT失效的唯一途径就是等token自己过期,无法做到主动让JWT失效。非要让JWT有主动失效的功能只能借助外力,即在服务端存储JWT的状态,在请求时添加判断逻辑,这个与JWT的无状态化、去中心化特性是矛盾的。但是,既然选择了JWT这条路,那就只能接受这个现实。
tips:我们目前项目使用的是JWT Token这种去中心化的架构,并且独立出了一个统一的资源服务器配置模块,详情可见:SpringCloud Alibaba微服务实战三十 | 统一资源服务器配置模块。
解决思路
上面说了,要实现JWT的主动失效需要借助外力,在服务端存储JWT的状态,一般使用Redis等高速缓存。而存储JWT状态又分为两种方案:
-
白名单机制
认证通过时,把JWT存到Redis中。注销时,从缓存移除JWT。请求资源添加判断JWT在缓存中是否存在,不存在拒绝访问。这种方式和cookie/session机制中的会话失效删除session基本一致。
-
黑名单机制
注销登录时,缓存JWT至Redis,且缓存有效时间设置为JWT的有效期,请求资源时判断是否存在缓存的黑名单中,存在则拒绝访问。
白名单和黑名单的实现逻辑差不多,黑名单不需每次登录都将JWT缓存,仅仅在某些特殊场景下需要缓存JWT,给服务器带来的压力要远远小于白名单的方式。
我更倾向于使用黑名单机制,有两个原因:
一是会大大节省Redis的存储空间,我们甚至都不需要存储完整的jwt,只需要存储jwt中的唯一id jti即可。