Spring OAuth2应用报错:找不到OAuth2AuthorizedClientService的唯一构造方法
Spring OAuth2 获取AccessToken时构造器异常问题
问题详情
在简单Spring OAuth2应用中尝试获取access token,使用OAuth2AuthorizedClientService调用/test端点时触发以下错误:
java.lang.IllegalStateException: No primary or single unique constructor found for interface org.springframework.security.oauth2.client.OAuth2AuthorizedClientService at org.springframework.beans.BeanUtils.getResolvableConstructor(BeanUtils.java:267) ~[spring-beans-5.3.21.jar:5.3.21] at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.createAttribute(ModelAttributeMethodProcessor.java:219) ~[spring-web-5.3.21.jar:5.3.21] at org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.createAttribute(ServletModelAttributeMethodProcessor.java:85) ~[spring-webmvc-5.3.22.jar:5.3.22] at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:147) ~[spring-web-5.3.21.jar:5.3.21] at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122) ~[spring-web-5.3.21.jar:5.3.21] at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:179) ~[spring-web-5.3.21.jar:5.3.21] at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:146) ~[spring-web-5.3.21.jar:5.3.21] at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117) ~[spring-webmvc-5.3.22.jar:5.3.22] ...
项目结构包含主类、配置类、控制器,两个端点:/home(允许所有访问)、/test(需认证),错误在请求/test时触发。
配置类代码
@Configuration public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeRequests() .mvcMatchers("/test/**").authenticated() .mvcMatchers("/home/**").permitAll() .and().oauth2Login(); return http.build(); } private ClientRegistration clientRegistration() { return CommonOAuth2Provider.GITHUB.getBuilder("github").clientId("<not sharing>") .clientSecret("<not sharing>") .scope("user:email").build(); } @Bean public ClientRegistrationRepository clientRepository() { ClientRegistration clientReg = clientRegistration(); return new InMemoryClientRegistrationRepository(clientReg); } }
控制器代码
@RestController public class TestController { @GetMapping("/home") public String home() { return "home page"; } @GetMapping("/test") public OAuth2AuthenticationToken testToken(OAuth2AuthenticationToken token, OAuth2AuthorizedClientService svc) { var t = svc.loadAuthorizedClient(token.getAuthorizedClientRegistrationId(), token.getName()); System.out.println(t.getAccessToken().getTokenValue()); return token; } }
依赖配置
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>5.7.2</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>5.7.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-client</artifactId> <version>2.7.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.7.2</version> </dependency>
使用JDK版本:jdk-16.0.1
解决方案
错误核心是控制器方法直接将OAuth2AuthorizedClientService作为参数传入,Spring MVC会把它当作@ModelAttribute尝试实例化,但它是接口,没有可实例化的构造器,因此抛出异常。
正确做法是通过依赖注入获取实例,而非作为方法参数:
修改后的控制器代码
@RestController public class TestController { // 构造器注入OAuth2AuthorizedClientService(Spring推荐方式) private final OAuth2AuthorizedClientService authorizedClientService; public TestController(OAuth2AuthorizedClientService authorizedClientService) { this.authorizedClientService = authorizedClientService; } @GetMapping("/home") public String home() { return "home page"; } @GetMapping("/test") public OAuth2AuthenticationToken testToken(OAuth2AuthenticationToken token) { var authorizedClient = authorizedClientService.loadAuthorizedClient( token.getAuthorizedClientRegistrationId(), token.getName() ); System.out.println(authorizedClient.getAccessToken().getTokenValue()); return token; } }
补充说明:spring-boot-starter-oauth2-client已自动配置OAuth2AuthorizedClientService的实现类(InMemoryOAuth2AuthorizedClientService),无需手动配置该Bean,直接注入即可使用。
内容的提问来源于stack exchange,提问作者Krusty the Clown
相关产品推荐
相关产品推荐

