使用spring-security-oauth2刷新令牌时丢失认证详情问题求助
我之前也踩过这个一模一样的坑!确实是DefaultTokenServices的refreshAccessToken方法里的默认逻辑有问题——刷新令牌生成新的access_token时,没有把旧令牌关联的OAuth2Authentication中的details字段传递过去,导致刷新后调用OAuth2Authentication.getDetails()拿不到之前的数据,而首次获取的令牌是正常的。
问题根源
默认的refreshAccessToken实现中,会基于刷新令牌重新构建OAuth2Authentication对象,但这个过程只复制了主体信息、权限列表等核心内容,完全忽略了details字段。这个字段通常用来存储客户端IP、请求上下文这类辅助信息,一旦丢失会影响依赖这些数据的业务逻辑。
可行的解决方案
因为官方相关PR一直没合并,而且旧版Spring Security OAuth2已经进入维护状态,最直接的办法就是自定义TokenServices来修复这个问题:
方式一:扩展DefaultTokenServices,手动保留details
重写refreshAccessToken方法,在生成新令牌后,把旧认证对象的details赋值给新的认证对象并重新存储:
import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2AccessToken; import org.springframework.security.oauth2.provider.token.DefaultTokenServices; import org.springframework.security.oauth2.provider.token.TokenRequest; public class CustomTokenServices extends DefaultTokenServices { @Override public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest) throws AuthenticationException { // 先调用父类方法完成基础刷新逻辑 OAuth2AccessToken newAccessToken = super.refreshAccessToken(refreshTokenValue, tokenRequest); if (newAccessToken == null) { return null; } // 获取旧刷新令牌对应的原始认证信息 OAuth2Authentication oldAuth = getAuthentication(refreshTokenValue); if (oldAuth != null && oldAuth.getDetails() != null) { // 获取新access_token对应的认证对象 OAuth2Authentication newAuth = getAuthentication(newAccessToken.getValue()); if (newAuth != null) { // 复制旧的details到新认证对象 newAuth.setDetails(oldAuth.getDetails()); // 重新存储更新后的认证信息 storeAuthentication(newAuth, newAccessToken); } } return newAccessToken; } }
之后在你的OAuth2配置类里,把这个自定义的CustomTokenServices注入替换默认的DefaultTokenServices即可。
方式二:迁移到Spring Security 5+的OAuth2模块
如果你的项目有升级空间,建议直接迁移到Spring Security 5官方的OAuth2支持(依赖spring-security-oauth2-client和spring-security-oauth2-resource-server)。新版的OAuth2实现已经重构了令牌刷新逻辑,从根本上避免了这类细节丢失的问题,而且官方维护更积极。
关于那个未合并的PR
你提到的那个PR确实是针对这个问题的修复,但因为旧版Spring Security OAuth2已经不再是官方主推的方案(官方转向了Spring Security 5的原生OAuth2支持),所以这类PR大概率不会被合并了。也正因为如此,相关的反馈才比较少——大部分新项目都直接用新版了,老项目遇到问题的可能直接自己动手修复了。
内容的提问来源于stack exchange,提问作者diegocr

