oauth2客戶端登入報錯BeanCurrentlyInCreationException
阿新 • • 發佈:2022-05-16
目錄
1 問題描述
原始碼如下:
package com.ian.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Lazy; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.web.client.RestTemplate; import static org.springframework.security.config.Customizer.withDefaults; /** * @author Witt * @version 1.0.0 * @date 2022/5/16 */ @EnableWebSecurity public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity.authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated()) // .oauth2Login(withDefaults()); .oauth2Login(oauth2Login -> oauth2Login .authorizationEndpoint(withDefaults()) .redirectionEndpoint(withDefaults()) .tokenEndpoint(withDefaults()) .userInfoEndpoint(withDefaults())); } /*@Bean public RestTemplate restTemplate() { return new RestTemplate(); }*/ @Bean // @Lazy public ClientRegistrationRepository clientRegistrationRepository() { // 該構造器中可以放置多個 ClientRegistration return new InMemoryClientRegistrationRepository(giteeClientRegistration()); } private ClientRegistration githubClientRegistration() { return ClientRegistration.withRegistrationId("github") .clientId("810cb8e57c48403ba804") .clientSecret("b4c405151bf0dea5c8be46548fec730dcb339172") .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) // default value .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) // default value // .redirectUri("{baseUrl}/login/oauth2/code/{registrationId}") .redirectUri("{baseUrl}/{action}/oauth2/code/{registrationId}") // default value // .scope("openid", "profile", "email", "address", "phone") .scope("read:user") // default value .authorizationUri("https://github.com/login/oauth/authorize") // default value .tokenUri("https://github.com/login/oauth/access_token") // default value .userInfoUri("https://api.github.com/user") // default value // .userNameAttributeName(IdTokenClaimNames.SUB) .userNameAttributeName("id") // default value // .jwkSetUri("") .clientName("Github") // default value .build(); } private ClientRegistration giteeClientRegistration() { return ClientRegistration.withRegistrationId("gitee") .clientId("de91bdadc6b6b3599bebfa8755850fca0844aae44f13cc5e076110e961d4b59e") .clientSecret("6baa392358c93949b4f5e9e443dfa646746eb40c67ffa3eb87decb408d500b77") .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) // default value .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) // default value // .redirectUri("{baseUrl}/login/oauth2/code/{registrationId}") // .redirectUri("{baseUrl}/{action}/oauth2/code/{registrationId}") // default value .redirectUri("http://localhost:8050/authorization_code") // 自定義方法 // .scope("openid", "profile", "email", "address", "phone") // .scope("user") // default value,但是報錯:存在錯誤,請求範圍無效、未知或格式不正確 .authorizationUri("https://gitee.com/oauth/authorize") // default value .tokenUri("https://gitee.com/oauth/token") // default value .userInfoUri("https://gitee.com/api/v5/user") // default value // .userNameAttributeName(IdTokenClaimNames.SUB) .userNameAttributeName("id") // default value // .jwkSetUri("") .clientName("Gitee") // default value .build(); } }
報錯內容:
2022-05-16 10:56:44.869 WARN 13271 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'OAuth2LoginSecurityConfig': Unsatisfied dependency expressed through method 'setContentNegotationStrategy' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration': Unsatisfied dependency expressed through method 'setConfigurers' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.OAuth2ClientConfiguration$OAuth2ClientWebMvcSecurityConfiguration': Unsatisfied dependency expressed through method 'setClientRegistrationRepository' parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'OAuth2LoginSecurityConfig': Requested bean is currently in creation: Is there an unresolvable circular reference? 2022-05-16 10:56:44.872 INFO 13271 --- [ main] o.apache.catalina.core.StandardService : Stopping service [Tomcat] 2022-05-16 10:56:44.885 INFO 13271 --- [ main] ConditionEvaluationReportLoggingListener : Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled. 2022-05-16 10:56:44.900 ERROR 13271 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPLICATION FAILED TO START *************************** Description: The dependencies of some of the beans in the application context form a cycle: ┌─────┐ | OAuth2LoginSecurityConfig ↑ ↓ | org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration ↑ ↓ | org.springframework.security.config.annotation.web.configuration.OAuth2ClientConfiguration$OAuth2ClientWebMvcSecurityConfiguration └─────┘ Action: Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
2 原因
原因是 OAuth2LoginSecurityConfig 在載入 ClientRegistration 物件時,ClientRegistrationRepository還沒有注入到Spring容器,然後內部一些載入機制,產生了迴圈依賴。
關於如何解決迴圈依賴,請參考這篇:https://blog.csdn.net/skh2015java/article/details/120957652
3 解決方案
我嘗試將 clientRegistrationRepository() 上的 @Bean 改為 @Lazy,程式是能正常啟動了,但是該 clientRegistrationRepository()返回的ClientRegistrationRepository物件,還是沒有注入到Spring容器中。
方案1:於是我將 ClientRegistrationRepository 的注入,單獨放到一個配置類中。解決問題。
方案2:以 ClientRegistrationRepository 的注入為主,建立配置類 OAuth2LoginConfig ,然後將 OAuth2LoginSecurityConfig 作為靜態內部類,放到該配置類OAuth2LoginConfig中。詳細程式碼如下:
package com.ian.config;
import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;
import org.springframework.web.client.RestTemplate;
import static org.springframework.security.config.Customizer.withDefaults;
/**
* 覆蓋預設的oauth2配置,以及覆蓋了 application.yml 中的配置
* 注:該類可以不存在,也能正常使用oauth2
*
* register a ClientRegistrationRepository @Bean
* @author Witt
* @version 1.0.0
* @date 2022/5/5
*/
@Configuration
public class OAuth2LoginConfig {
/**
* 覆蓋預設的oauth2程式碼配置,以及覆蓋了 application.yml 中的配置
* enable OAuth 2.0 login through httpSecurity.oauth2Login()
* 注:該類可以不存在,也能正常使用oauth2
*
* @author Witt
* @version 1.0.0
* @date 2022/5/5
*/
@EnableWebSecurity
public static class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated())
// .oauth2Login(withDefaults());
.oauth2Login(oauth2Login -> oauth2Login
.authorizationEndpoint(withDefaults())
.redirectionEndpoint(withDefaults())
.tokenEndpoint(withDefaults())
.userInfoEndpoint(withDefaults()));
}
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
public ClientRegistrationRepository clientRegistrationRepository() {
// 該構造器中可以放置多個 ClientRegistration
return new InMemoryClientRegistrationRepository(giteeClientRegistration());
}
private ClientRegistration githubClientRegistration() {
return ClientRegistration.withRegistrationId("github")
.clientId("810cb8e57c48403ba804")
.clientSecret("b4c405151bf0dea5c8be46548fec730dcb339172")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) // default value
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) // default value
// .redirectUri("{baseUrl}/login/oauth2/code/{registrationId}")
.redirectUri("{baseUrl}/{action}/oauth2/code/{registrationId}") // default value
// .scope("openid", "profile", "email", "address", "phone")
.scope("read:user") // default value
.authorizationUri("https://github.com/login/oauth/authorize") // default value
.tokenUri("https://github.com/login/oauth/access_token") // default value
.userInfoUri("https://api.github.com/user") // default value
// .userNameAttributeName(IdTokenClaimNames.SUB)
.userNameAttributeName("id") // default value
// .jwkSetUri("")
.clientName("Github") // default value
.build();
}
private ClientRegistration giteeClientRegistration() {
return ClientRegistration.withRegistrationId("gitee")
.clientId("de91bdadc6b6b3599bebfa8755850fca0844aae44f13cc5e076110e961d4b59e")
.clientSecret("6baa392358c93949b4f5e9e443dfa646746eb40c67ffa3eb87decb408d500b77")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) // default value
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) // default value
// .redirectUri("{baseUrl}/login/oauth2/code/{registrationId}")
// .redirectUri("{baseUrl}/{action}/oauth2/code/{registrationId}") // default value
.redirectUri("http://localhost:8050/authorization_code") //
// .scope("openid", "profile", "email", "address", "phone")
// .scope("user") // default value,但是報錯:存在錯誤,請求範圍無效、未知或格式不正確
.authorizationUri("https://gitee.com/oauth/authorize") // default value
.tokenUri("https://gitee.com/oauth/token") // default value
.userInfoUri("https://gitee.com/api/v5/user") // default value
// .userNameAttributeName(IdTokenClaimNames.SUB)
.userNameAttributeName("id") // default value
// .jwkSetUri("")
.clientName("Gitee") // default value
.build();
}
}