英文:
AlreadyBuiltException while configuring multiple HttpSecurity instances with Spring Security
问题
我正在使用Spring v.5.3.28和Spring Security v.5.8.4,我尝试配置Spring Security而不扩展WebSecurityConfigurerAdapter,因为它已被弃用。
我的Spring Security配置如下:
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration {
private static final String SIGNIN = "/signin";
@Autowired
ApiCustomAuthenticationProviderImpl apiCustomAuthenticationProvider;
@Autowired
CustomAuthenticationProviderImpl customAuthenticationProvider;
@Autowired
LdapAuthenticationProviderImpl ldapAuthenticationProvider;
@Autowired
SautenticaAuthenticationProviderImpl sautenticaAuthenticationProvider;
@Autowired
ConfigurationService configurationService;
@Bean
protected UserDetailsService userDetailsService () {
return new AutenticacionServiceImpl();
}
@Bean
public EvaluationContextExtension securityExtension () {
return new SecurityEvaluationContextExtension();
}
@Bean
public AuditorAware<Usuario> auditorAware () {
return new SpringSecurityAuditorAware();
}
public AuthenticationManager getAuthenticationManager (HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
final String authMode = configurationService.findByCode(Constants.CT_AUTH_MODE);
if (StringUtils.isNotEmpty(authMode) && Constants.CT_LDAP.equalsIgnoreCase(authMode)) {
authenticationManagerBuilder.authenticationProvider(ldapAuthenticationProvider);
} else if (StringUtils.isNotEmpty(authMode) && Constants.CT_SAUTENTICA.equalsIgnoreCase(authMode)) {
authenticationManagerBuilder.authenticationProvider(sautenticaAuthenticationProvider);
} else {
authenticationManagerBuilder.authenticationProvider(customAuthenticationProvider);
}
authenticationManagerBuilder.eraseCredentials(false);
return authenticationManagerBuilder.build();
}
@Bean
@Order(1)
public SecurityFilterChain apiSecurityFilterChain (HttpSecurity http) throws Exception {
try {
http.securityMatcher("/api-ws/**");
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeHttpRequests().requestMatchers("/api-ws/**").authenticated().and().httpBasic().and().csrf().disable()
.addFilterAfter(new ApiCustomBasicAuthenticationFilter(), BasicAuthenticationFilter.class);
http.authenticationProvider(apiCustomAuthenticationProvider);
} catch (final Exception e) {
throw new SecurityException(e);
}
return http.build();
}
@Bean
@Order(2)
public SecurityFilterChain wsSecurityFilterChain (HttpSecurity http) throws Exception {
try {
http.securityMatcher("/ws/**");
http.authorizeHttpRequests().requestMatchers("/ws/patient/**", "/meet-ws/case/**").authenticated();
http.httpBasic().and().addFilterAt(new CustomBasicAuthenticationFilter(getAuthenticationManager(http)), BasicAuthenticationFilter.class);
http.headers().frameOptions().disable();
} catch (final Exception e) {
throw a SecurityException(e);
}
return http.build();
}
@Bean
public WebSecurityCustomizer webSecurityCustomizer () {
return web -> web.ignoring().requestMatchers("/ws/login**", "/ws/new/xml**");
}
@Bean
public SecurityFilterChain securityFilterChain (HttpSecurity http) throws Exception {
try {
http.authorizeHttpRequests().requestMatchers("/favicon.ico", "/resources/**", "/health**", "/signin**", "/authenticate**").permitAll().anyRequest().authenticated()
.and().authenticationManager(getAuthenticationManager(http)).formLogin().loginPage(SIGNIN).permitAll().loginProcessingUrl("/authenticate").permitAll()
.failureUrl("/signin?error=1").successHandler(new CustomAuthenticationSuccessHandler()).and().sessionManagement().invalidSessionUrl(SIGNIN).and()
.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler()).and().headers().frameOptions().disable();
http.logout().logoutUrl("/logout").logoutSuccessUrl(SIGNIN).permitAll();
} catch (final Exception e) {
throw new SecurityException(e);
}
return http.build();
}
}
当我构建项目时,它构建成功,但当我启动服务器时,它抛出以下异常:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration': Unsatisfied dependency expressed through method 'setFilterChains' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'wsSecurityFilterChain' defined in es.mycompany.myapp.SecurityConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.web.SecurityFilterChain]: Factory method 'wsSecurityFilterChain' threw exception; nested exception is org.springframework.security.config.annotation.AlreadyBuiltException: This object has already been built
...
我唯一能想到的是http.build();
被调用两次,但它们在文档中执行的是完全相同的操作:https://docs.spring.io/spring-security/reference/servlet/configuration/java.html#_multiple_httpsecurity_instances
英文:
I'm using Spring v.5.3.28 and Spring Security v.5.8.4, and I'm trying to configure Spring Security without extending WebSecurityConfigurerAdapter as it has been deprecated.
My Spring Security configuration looks like this:
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration {
private static final String SIGNIN = "/signin";
@Autowired
ApiCustomAuthenticationProviderImpl apiCustomAuthenticationProvider;
@Autowired
CustomAuthenticationProviderImpl customAuthenticationProvider;
@Autowired
LdapAuthenticationProviderImpl ldapAuthenticationProvider;
@Autowired
SautenticaAuthenticationProviderImpl sautenticaAuthenticationProvider;
@Autowired
ConfigurationService configurationService;
@Bean
protected UserDetailsService userDetailsService () {
return new AutenticacionServiceImpl();
}
@Bean
public EvaluationContextExtension securityExtension () {
return new SecurityEvaluationContextExtension();
}
@Bean
public AuditorAware<Usuario> auditorAware () {
return new SpringSecurityAuditorAware();
}
public AuthenticationManager getAuthenticationManager (HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
final String authMode = configurationService.findByCode(Constants.CT_AUTH_MODE);
if (StringUtils.isNotEmpty(authMode) && Constants.CT_LDAP.equalsIgnoreCase(authMode)) {
authenticationManagerBuilder.authenticationProvider(ldapAuthenticationProvider);
} else if (StringUtils.isNotEmpty(authMode) && Constants.CT_SAUTENTICA.equalsIgnoreCase(authMode)) {
authenticationManagerBuilder.authenticationProvider(sautenticaAuthenticationProvider);
} else {
authenticationManagerBuilder.authenticationProvider(customAuthenticationProvider);
}
authenticationManagerBuilder.eraseCredentials(false);
return authenticationManagerBuilder.build();
}
@Bean
@Order(1)
public SecurityFilterChain apiSecurityFilterChain (HttpSecurity http) throws Exception {
try {
http.securityMatcher("/api-ws/**");
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeHttpRequests().requestMatchers("/api-ws/**").authenticated().and().httpBasic().and().csrf().disable()
.addFilterAfter(new ApiCustomBasicAuthenticationFilter(), BasicAuthenticationFilter.class);
http.authenticationProvider(apiCustomAuthenticationProvider);
} catch (final Exception e) {
throw new SecurityException(e);
}
return http.build();
}
@Bean
@Order(2)
public SecurityFilterChain wsSecurityFilterChain (HttpSecurity http) throws Exception {
try {
http.securityMatcher("/ws/**");
http.authorizeHttpRequests().requestMatchers("/ws/patient/**", "/meet-ws/case/**").authenticated();
http.httpBasic().and().addFilterAt(new CustomBasicAuthenticationFilter(getAuthenticationManager(http)), BasicAuthenticationFilter.class);
http.headers().frameOptions().disable();
} catch (final Exception e) {
throw new SecurityException(e);
}
return http.build();
}
@Bean
public WebSecurityCustomizer webSecurityCustomizer () {
return web -> web.ignoring().requestMatchers("/ws/login**", "/ws/new/xml**");
}
@Bean
public SecurityFilterChain securityFilterChain (HttpSecurity http) throws Exception {
try {
http.authorizeHttpRequests().requestMatchers("/favicon.ico", "/resources/**", "/health**", "/signin**", "/authenticate**").permitAll().anyRequest().authenticated()
.and().authenticationManager(getAuthenticationManager(http)).formLogin().loginPage(SIGNIN).permitAll().loginProcessingUrl("/authenticate").permitAll()
.failureUrl("/signin?error=1").successHandler(new CustomAuthenticationSuccessHandler()).and().sessionManagement().invalidSessionUrl(SIGNIN).and()
.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler()).and().headers().frameOptions().disable();
http.logout().logoutUrl("/logout").logoutSuccessUrl(SIGNIN).permitAll();
} catch (final Exception e) {
throw new SecurityException(e);
}
return http.build();
}
}
When I build the project it builds successfully but when I start the server it throws the following exception:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration': Unsatisfied dependency expressed through method 'setFilterChains' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'wsSecurityFilterChain' defined in es.mycompany.myapp.SecurityConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.web.SecurityFilterChain]: Factory method 'wsSecurityFilterChain' threw exception; nested exception is org.springframework.security.config.annotation.AlreadyBuiltException: This object has already been built
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:768)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:720)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:920)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:399)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:278)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:103)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4766)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5230)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1396)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1386)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134)
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:919)
at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:835)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1396)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1386)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134)
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:919)
at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:263)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.StandardService.startInternal(StandardService.java:432)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:927)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.startup.Catalina.start(Catalina.java:772)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:345)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:476)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'wsSecurityFilterChain' defined in es.mycompany.myapp.SecurityConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.web.SecurityFilterChain]: Factory method 'wsSecurityFilterChain' threw exception; nested exception is org.springframework.security.config.annotation.AlreadyBuiltException: This object has already been built
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:658)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:638)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1352)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1195)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1609)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1573)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1462)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1349)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1311)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:760)
... 46 more
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.web.SecurityFilterChain]: Factory method 'wsSecurityFilterChain' threw exception; nested exception is org.springframework.security.config.annotation.AlreadyBuiltException: This object has already been built
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653)
... 62 more
Caused by: org.springframework.security.config.annotation.AlreadyBuiltException: This object has already been built
at org.springframework.security.config.annotation.AbstractSecurityBuilder.build(AbstractSecurityBuilder.java:41)
at org.springframework.security.config.annotation.web.builders.HttpSecurity.beforeConfigure(HttpSecurity.java:3225)
at org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder.doBuild(AbstractConfiguredSecurityBuilder.java:308)
at org.springframework.security.config.annotation.AbstractSecurityBuilder.build(AbstractSecurityBuilder.java:38)
at es.mycompany.myapp.SecurityConfiguration.wsSecurityFilterChain(SecurityConfiguration.java:152)
at es.mycompany.myapp.SecurityConfiguration$$EnhancerBySpringCGLIB$$aaa83f30.CGLIB$wsSecurityFilterChain$7(<generated>)
at es.mycompany.myapp.SecurityConfiguration$$EnhancerBySpringCGLIB$$aaa83f30$$FastClassBySpringCGLIB$$ed15352b.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331)
at es.mycompany.myapp.SecurityConfiguration$$EnhancerBySpringCGLIB$$aaa83f30.wsSecurityFilterChain(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
... 63 more
The only thing that I can think of is http.build(); being called twice, but they do the exact same thing in the documentation: https://docs.spring.io/spring-security/reference/servlet/configuration/java.html#_multiple_httpsecurity_instances
答案1
得分: 1
你收到的错误是因为你自己调用了 authenticationManagerBuilder.build()
。你正在访问构建器的内部框架实例并自己调用 build()
,而 Spring Security 也会在内部构建身份验证管理器。
通常,任何作为 @Bean
(或 @Component
等)的 AuthenticationProvider
都会被 Spring Security 自动装配。你应该重新构建你的应用程序,以便你可以有条件地将所需的 AuthenticationProvider
应用于相关的过滤器链(而不是发布为 beans)。整个 getAuthenticationManager
方法应该被移除。
英文:
The error you receive comes from calling authenticationManagerBuilder.build()
yourself. You are accessing an internal framework instance of the builder and calling build()
yourself, while Spring Security also builds the authentication manager internally.
Generally, any AuthenticationProvider
that is a @Bean
(or @Component
, etc.) will be autowired by Spring Security. You will want to restructure your application so you can conditionally apply the desired AuthenticationProvider
to the relevant filter chain (instead of publishing as beans). The entire getAuthenticationManager
method should be removed.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论