AlreadyBuiltException while configuring multiple HttpSecurity instances with Spring Security

huangapple go评论70阅读模式
英文:

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 = &quot;/signin&quot;;
@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&lt;Usuario&gt; 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) &amp;&amp; Constants.CT_LDAP.equalsIgnoreCase(authMode)) {
authenticationManagerBuilder.authenticationProvider(ldapAuthenticationProvider);
} else if (StringUtils.isNotEmpty(authMode) &amp;&amp; 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(&quot;/api-ws/**&quot;);
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeHttpRequests().requestMatchers(&quot;/api-ws/**&quot;).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(&quot;/ws/**&quot;);
http.authorizeHttpRequests().requestMatchers(&quot;/ws/patient/**&quot;, &quot;/meet-ws/case/**&quot;).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 -&gt; web.ignoring().requestMatchers(&quot;/ws/login**&quot;, &quot;/ws/new/xml**&quot;);
}
@Bean
public SecurityFilterChain securityFilterChain (HttpSecurity http) throws Exception {
try {
http.authorizeHttpRequests().requestMatchers(&quot;/favicon.ico&quot;, &quot;/resources/**&quot;, &quot;/health**&quot;, &quot;/signin**&quot;, &quot;/authenticate**&quot;).permitAll().anyRequest().authenticated()
.and().authenticationManager(getAuthenticationManager(http)).formLogin().loginPage(SIGNIN).permitAll().loginProcessingUrl(&quot;/authenticate&quot;).permitAll()
.failureUrl(&quot;/signin?error=1&quot;).successHandler(new CustomAuthenticationSuccessHandler()).and().sessionManagement().invalidSessionUrl(SIGNIN).and()
.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler()).and().headers().frameOptions().disable();
http.logout().logoutUrl(&quot;/logout&quot;).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 &#39;org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration&#39;: Unsatisfied dependency expressed through method &#39;setFilterChains&#39; parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name &#39;wsSecurityFilterChain&#39; 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 &#39;wsSecurityFilterChain&#39; 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 &#39;wsSecurityFilterChain&#39; 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 &#39;wsSecurityFilterChain&#39; 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 &#39;wsSecurityFilterChain&#39; 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(&lt;generated&gt;)
at es.mycompany.myapp.SecurityConfiguration$$EnhancerBySpringCGLIB$$aaa83f30$$FastClassBySpringCGLIB$$ed15352b.invoke(&lt;generated&gt;)
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(&lt;generated&gt;)
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.

huangapple
  • 本文由 发表于 2023年7月13日 18:24:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/76678336.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定