Spring Authorization Server中自定义OAuth2AuthorizationService是否可仅存储JWT场景下的刷新令牌?
你的思路完全在线,先给你吃个定心丸:默认的OAuth2AuthorizationService实现存储JWT访问令牌,本质上是为了覆盖所有可能的配置场景——毕竟Spring Authorization Server要兼容opaque访问令牌的情况,这种场景下必须存令牌到服务器端才能验证。而你用自包含JWT的场景,确实不需要存储JWT字符串本身,优化存储是完全合理的。
不过这里有几个关键点需要你注意,避免踩坑:
1. OAuth2Authorization不止是存令牌字符串
你可能误解了OAuth2Authorization对象的作用:它本质上是授权上下文的快照,而不只是令牌的容器。里面除了令牌字符串,还包含了:
- 用户的授权范围(scopes)
- 客户端的ID和关联信息
- 授权的创建/过期时间
- 授权的状态(比如是否被主动撤销)
- 刷新令牌的绑定关系
这些上下文信息才是服务器在后续流程(比如处理刷新令牌请求、验证授权有效性)中真正需要的,而JWT访问令牌只是这个上下文的“序列化输出”。所以即使你不存JWT字符串,也需要把这些核心上下文信息存在OAuth2Authorization里,不然Spring Authorization Server的流程会断片。
2. 完全可以不存储JWT访问令牌的字符串
针对你的场景,自定义OAuth2AuthorizationService时,你只需要:
- 存储刷新令牌的完整信息(因为它是opaque的,必须服务器端验证)
- 存储OAuth2Authorization的核心上下文(用户ID、客户端信息、授权范围、过期时间等)
- 对于
accessToken字段,不需要存储JWT的实际字符串,甚至可以只填充元数据(比如tokenId、expiresAt、令牌类型),留空token值
这样既节省了Redis的存储空间,又不影响Spring Authorization Server的正常运行——因为当需要签发新的JWT访问令牌时,服务器可以根据上下文信息重新生成;而处理刷新请求时,只需要验证刷新令牌和授权上下文的有效性即可。
3. 需要注意的边缘场景
虽然大部分流程没问题,但有两个场景要额外处理:
- 令牌 introspection 端点(如果开启):如果你的服务开放了RFC7662定义的令牌 introspection 接口,客户端可能会用它来验证JWT的有效性。这时候服务器需要从OAuth2Authorization中读取授权上下文来返回状态(比如是否有效、范围、过期时间),但依然不需要存储JWT字符串本身,只要上下文信息完整就行。
- 主动撤销授权:如果用户主动撤销了某个客户端的授权,或者你的系统禁用了该用户,即使JWT还没过期,当用刷新令牌请求新的访问令牌时,服务器会检查OAuth2Authorization的状态并拒绝请求。这一步依赖的也是上下文信息,和JWT字符串无关。
总结
你的优化思路非常合理,完全不需要为了兼容默认实现而浪费Redis空间。只要自定义的OAuth2AuthorizationService能正确维护授权上下文,只存储刷新令牌和必要的元数据,不存储JWT访问令牌字符串是完全可行的——默认实现存两者只是为了“兜底”,适配所有配置场景而已。
备注:内容来源于stack exchange,提问作者leo

