Spring Boot 3.3集成Google OAuth2时refresh_token为空问题
解决方案:Spring Boot 3.3 OAuth2 Google登录无法获取Refresh Token
问题根源分析
Google OAuth2仅在首次授权(用户首次同意权限请求)时返回Refresh Token,且必须满足两个核心条件:
- 明确指定
access_type=offline,告知Google需要离线访问权限 - 若用户已授权过相同权限,需强制触发重新授权(添加
prompt=consent)才会再次返回Refresh Token
此外,你的配置和代码存在两处关键问题导致无法获取Refresh Token:
步骤1:修正OAuth2客户端配置
不要手动拼接authorization-uri参数,改用Spring Boot官方提供的配置属性来设置离线访问和授权提示,避免配置兼容问题:
修改application.yaml:
spring: security: oauth2: client: registration: google: client-id: xxxx client-secret: xx scope: profile, email, openid authorization-grant-type: authorization_code redirect-uri: http://localhost:8080/login/oauth2/code/google client-name: Google # 开启离线访问,获取Refresh Token的核心配置 access-type: offline # 强制用户重新授权,确保首次/强制授权时返回Refresh Token(开发阶段可用,生产按需调整) authorization-request-parameters: prompt: consent provider: google: # 恢复官方默认授权地址,由Spring自动拼接参数 authorization-uri: https://accounts.google.com/o/oauth2/v2/auth token-uri: https://oauth2.googleapis.com/token user-info-uri: https://www.googleapis.com/oauth2/v3/userinfo user-name-attribute: sub
配置说明:
access-type: offline:明确告知Google需要离线访问权限,这是获取Refresh Token的必要条件prompt: consent:强制用户每次登录都重新确认权限,确保Google返回Refresh Token(生产环境可移除,仅首次授权时触发)
步骤2:修正代码中Principal的获取逻辑
OAuth2AuthorizedClientService.loadAuthorizedClient()需要的是用户的唯一标识(对应OIDC的sub字段,即Authentication的name属性),而非用户的显示名称。同时补充完整的Refresh Token刷新逻辑:
修改OAuth2TokenService的refreshToken方法:
@Service public class OAuth2TokenService { @Autowired private OAuth2AuthorizedClientService authorizedClientService; @Autowired private ClientRegistrationRepository clientRegistrationRepository; private final RestTemplate restTemplate = new RestTemplate(); public OAuth2AccessToken refreshToken(Authentication authentication) { if (!(authentication instanceof OAuth2AuthenticationToken)) { throw new IllegalArgumentException("Invalid authentication type"); } // 直接使用Authentication的name(用户唯一标识,对应OIDC的sub字段) String principalName = authentication.getName(); final String registrationId = "google"; OAuth2AuthorizedClient authorizedClient = authorizedClientService.loadAuthorizedClient(registrationId, principalName); OAuth2RefreshToken refreshToken = authorizedClient.getRefreshToken(); if (refreshToken == null) { throw new IllegalArgumentException("No refresh token found"); } // 执行Refresh Token刷新操作 ClientRegistration clientRegistration = clientRegistrationRepository.findByRegistrationId(registrationId); OAuth2RefreshTokenGrantRequest refreshRequest = new OAuth2RefreshTokenGrantRequest( clientRegistration, authorizedClient.getAccessToken(), refreshToken ); OAuth2AccessTokenResponse tokenResponse = new DefaultAuthorizationCodeTokenResponseClient() .getTokenResponse(refreshRequest); // 更新存储中的授权客户端信息 authorizedClientService.saveAuthorizedClient( new OAuth2AuthorizedClient( clientRegistration, principalName, tokenResponse.getAccessToken(), tokenResponse.getRefreshToken() ) ); return tokenResponse.getAccessToken(); } }
代码修正点:
- 简化Authentication类型校验逻辑
- 用
authentication.getName()获取用户唯一标识,避免手动转换OidcUser可能出现的错误 - 补充完整的Refresh Token刷新流程,并更新存储的授权客户端信息
步骤3:测试验证
- 清除浏览器中Google的登录状态(或使用隐身窗口),确保触发首次授权流程
- 启动应用,访问登录页面完成Google OAuth2授权
- 调用
refreshToken方法,确认Refresh Token已被正确获取
内容的提问来源于stack exchange,提问作者Alok Mishra
相关产品推荐
相关产品推荐

