Spring Boot集成IdentityServer4调用/connect/token传code_verifier报invalid_grant如何解决
问题根因
- 核心错误是将授权请求中的
code_challenge直接作为code_verifier传入token接口,违反PKCE(RFC7636)规范的校验逻辑 - 手动构造请求时未获取Spring Security OAuth2 Client自动生成的正确
code_verifier - 存在参数配置不匹配、请求参数拼接错误的问题
解决步骤
1. 修正PKCE参数的取值逻辑
PKCE的正确生成和使用规则如下,你当前的传值逻辑完全颠倒:
- 第一步:生成长度为43~128位的随机字符串作为原始
code_verifier,允许的字符范围为A-Z/a-z/0-9/-/./_/~ - 第二步:对
code_verifier做SHA256哈希运算后,再做Base64URL编码得到code_challenge,放到/connect/authorize授权请求中 - 第三步:调用/connect/token接口时传入原始未哈希的code_verifier,而非授权请求里的
code_challenge
2. 复用Spring Security自动生成的code_verifier
Spring Security OAuth2 Client的oauth2Login流程已经自动完成了code_verifier的生成、存储和token请求校验逻辑,如果你确实需要手动调用token接口,可以从会话中取出系统生成的正确code_verifier:
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; import javax.servlet.http.HttpServletRequest; // 在需要获取参数的方法中注入HttpServletRequest对象 OAuth2AuthorizationRequest authRequest = (OAuth2AuthorizationRequest) request.getSession() .getAttribute(OAuth2AuthorizationRequest.class.getName() + ".AUTHORIZATION_REQUEST"); String correctCodeVerifier = (String) authRequest.getAttribute("code_verifier");
将取到的correctCodeVerifier作为code_verifier参数的值传入token请求即可。
3. 修正其他错误
- 参数一致性:你的配置文件中
client-id为client_id_test,请求中传的client_id=client_id需修改为和配置一致的值 - 认证方法匹配:配置中
client-authentication-method: none为公共客户端模式,不需要传client_secret参数,调用token接口时删除该参数即可 - 请求格式修正:你当前的form参数拼接错误,
redirect_uri参数末尾缺少&符号,会导致后续参数解析失败,需补充该符号
4. 最佳实践:直接使用Spring封装的token获取方式
完全不需要手动构造RestTemplate调用token接口,直接注入OAuth2AuthorizedClientService即可直接拿到已校验完成的access token:
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; import org.springframework.security.core.Authentication; @Autowired private OAuth2AuthorizedClientService authorizedClientService; public String getAccessToken(Authentication authentication) { OAuth2AuthorizedClient client = authorizedClientService.loadAuthorizedClient( "idsrv4", authentication.getName() ); return client.getAccessToken().getTokenValue(); }
内容的提问来源于stack exchange,提问作者user1168880
相关产品推荐
相关产品推荐

