Spring集成项目中OAuth2授权码模式调用REST API遇阻求助
解决Spring集成OAuth2授权码模式调用REST服务的重定向异常问题
你遇到的UserRedirectRequiredException是授权码模式的正常流程提示——因为授权码模式必须经过用户手动登录并同意授权的步骤,直接在代码里调用obtainAuthorizationCode是跳不过这个交互环节的。下面我来帮你梳理正确的实现方式:
错误原因分析
授权码模式的完整流程是:
- 客户端引导用户跳转到授权服务器的授权页面(就是你配置的
userAuthorizationUri) - 用户登录并同意授权后,授权服务器会重定向到你配置的
preEstablishedRedirectUri,并携带授权码code - 客户端用这个
code加上客户端凭证,去授权服务器换取访问令牌access_token
你的代码直接调用authProvider.obtainAuthorizationCode(resource, request),但此时没有用户参与授权,所以框架抛出异常提示需要重定向到授权页面。
分场景解决方案
场景1:Web应用(推荐用Spring Security OAuth2 Client自动处理)
如果是Web项目,推荐用Spring Security的OAuth2客户端支持来自动处理重定向和授权流程,步骤如下:
- 配置OAuth2客户端信息(可以在
application.yml中):
spring: security: oauth2: client: registration: infusionsoft: client-id: xxxxxx client-secret: xxxxxx authorization-grant-type: authorization_code redirect-uri: "https://myapps.com:8181/my_work" scope: full provider: infusionsoft: authorization-uri: https://signin.infusionsoft.com/app/oauth/authorize token-uri: https://api.infusionsoft.com/token
- 配置Spring Security,启用OAuth2登录:
import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .oauth2Login() // 自动处理授权跳转、用户登录和回调逻辑 .defaultSuccessUrl("/your-api-entry"); // 授权成功后的跳转路径 } }
- 注入
OAuth2RestTemplate调用受保护API:
import org.springframework.security.oauth2.client.OAuth2RestTemplate; import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; import org.springframework.stereotype.Component; @Component public class InfusionsoftApiClient { private final OAuth2RestTemplate restTemplate; public InfusionsoftApiClient(OAuth2ProtectedResourceDetails resourceDetails) { this.restTemplate = new OAuth2RestTemplate(resourceDetails); } public String fetchProtectedData() { // 框架会自动处理令牌的获取、刷新和携带 return restTemplate.getForObject("https://api.infusionsoft.com/your-protected-endpoint", String.class); } }
场景2:命令行/桌面应用(手动处理授权码)
如果是像你写的main方法那样的命令行程序,因为没有Web服务器处理回调,需要手动获取授权码:
import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Scanner; import org.springframework.security.oauth2.client.token.AccessTokenRequest; import org.springframework.security.oauth2.client.token.DefaultAccessTokenRequest; import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider; import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.common.util.OAuth2Utils; public class OAuth2Client1 { public static void main(String[] args) { AuthorizationCodeResourceDetails resource = new AuthorizationCodeResourceDetails(); resource.setClientId("xxxxxx"); resource.setClientSecret("xxxxxx"); resource.setAccessTokenUri("https://api.infusionsoft.com/token"); resource.setUserAuthorizationUri("https://signin.infusionsoft.com/app/oauth/authorize"); resource.setPreEstablishedRedirectUri("https://myapps.com:8181/my_work"); resource.setScope(Arrays.asList("full")); // 1. 生成授权URL,让用户手动打开浏览器访问 Map<String, String> params = new HashMap<>(); params.put(OAuth2Utils.CLIENT_ID, resource.getClientId()); params.put(OAuth2Utils.REDIRECT_URI, resource.getPreEstablishedRedirectUri()); params.put(OAuth2Utils.SCOPE, String.join(",", resource.getScope())); params.put(OAuth2Utils.RESPONSE_TYPE, "code"); String authorizationUrl = OAuth2Utils.formatQueryString(params, resource.getUserAuthorizationUri()); System.out.println("请打开以下链接完成授权:"); System.out.println(authorizationUrl); // 2. 让用户复制回调URL中的code参数输入到程序 Scanner scanner = new Scanner(System.in); System.out.println("请输入授权码:"); String code = scanner.nextLine(); // 3. 用授权码换取访问令牌 try { AuthorizationCodeAccessTokenProvider authProvider = new AuthorizationCodeAccessTokenProvider(); AccessTokenRequest request = new DefaultAccessTokenRequest(); request.setAuthorizationCode(code); request.setRedirectUri(resource.getPreEstablishedRedirectUri()); OAuth2AccessToken token = authProvider.obtainAccessToken(resource, request); System.out.println("获取到的访问令牌:" + token.getValue()); // 后续可以用这个token调用受保护的REST服务 } catch (Exception e) { e.printStackTrace(); } } }
额外注意点
- 确保你的
preEstablishedRedirectUri已经在授权服务器(Infusionsoft)的应用配置中注册过,否则会被拒绝回调 - Web应用中不要手动调用
obtainAuthorizationCode,交给Spring Security自动处理流程更可靠 - 生产环境中,不要硬编码客户端密钥
clientSecret,建议通过配置中心或环境变量注入
内容的提问来源于stack exchange,提问作者user1516815
相关产品推荐
相关产品推荐

