Spring-security: SEC-2510: "This object has already been built"

Created on 6 Mar 2014  路  10Comments  路  Source: spring-projects/spring-security

Vernon (Migrated from SEC-2510) said:

It only occurs for 3.2.1, but not 3.2.0. Here is the exception stack during a start-up:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springSecurityFilterChain' defined in class org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration: Instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public javax.servlet.Filter org.springframework.security.config.a
        at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:592)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1094)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:989)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
        at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:700)
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
        at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:381)
        at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:293)
        at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)
        at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4937)
        at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5434)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
        at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
        at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:744)
Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public javax.servlet.Filter org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration.springSecurityFilterChain() throws java.lang.Exception] 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:188)
        at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:581)
        ... 23 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:42)
        at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter.authenticationManager(WebSecurityConfigurerAdapter.java:228)
        at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter.getHttp(WebSecurityConfigurerAdapter.java:168)
        at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter.init(WebSecurityConfigurerAdapter.java:273)
        at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter.init(WebSecurityConfigurerAdapter.java:58)
        at com.vernonwu.vsm.config.WebSecurityConfig$$EnhancerByCGLIB$$94ba9b96.init(<generated>)
        at org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder.init(AbstractConfiguredSecurityBuilder.java:369)
        at org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder.doBuild(AbstractConfiguredSecurityBuilder.java:322)
        at org.springframework.security.config.annotation.AbstractSecurityBuilder.build(AbstractSecurityBuilder.java:39)
        at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration.springSecurityFilterChain(WebSecurityConfiguration.java:92)
        at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$$EnhancerByCGLIB$$e0d2ce43.CGLIB$springSecurityFilterChain$0(<generated>)
        at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$$EnhancerByCGLIB$$e0d2ce43$$FastClassByCGLIB$$274f7312.invoke(<generated>)
        at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
        at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:326)
        at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$$EnhancerByCGLIB$$e0d2ce43.springSecurityFilterChain(<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:483)
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:166)
        ... 24 more
Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springSecurityFilterChain' defined in class org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration: Instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public javax.servlet.Filter org.springframework.security.config.a
        at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:592)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1094)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:989)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
        at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:700)
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
        at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:381)
        at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:293)
        at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)
        at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4937)
        at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5434)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
        at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
        at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:744)
Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public javax.servlet.Filter org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration.springSecurityFilterChain() throws java.lang.Exception] 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:188)
        at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:581)
        ... 23 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:42)
        at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter.authenticationManager(WebSecurityConfigurerAdapter.java:228)
        at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter.getHttp(WebSecurityConfigurerAdapter.java:168)
        at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter.init(WebSecurityConfigurerAdapter.java:273)
        at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter.init(WebSecurityConfigurerAdapter.java:58)
        at com.vernonwu.vsm.config.WebSecurityConfig$$EnhancerByCGLIB$$94ba9b96.init(<generated>)
        at org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder.init(AbstractConfiguredSecurityBuilder.java:369)
        at org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder.doBuild(AbstractConfiguredSecurityBuilder.java:322)
        at org.springframework.security.config.annotation.AbstractSecurityBuilder.build(AbstractSecurityBuilder.java:39)
        at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration.springSecurityFilterChain(WebSecurityConfiguration.java:92)
        at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$$EnhancerByCGLIB$$e0d2ce43.CGLIB$springSecurityFilterChain$0(<generated>)
        at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$$EnhancerByCGLIB$$e0d2ce43$$FastClassByCGLIB$$274f7312.invoke(<generated>)
        at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
        at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:326)
        at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$$EnhancerByCGLIB$$e0d2ce43.springSecurityFilterChain(<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:483)
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:166)
        ... 24 more
Error listenerStart
Context [] startup failed due to previous errors
The web application [] registered the JDBC driver [org.postgresql.Driver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.
Failed to check for ThreadLocal references for web application []
java.lang.NullPointerException
        at org.apache.catalina.loader.WebappClassLoader.loadedByThisOrChild(WebappClassLoader.java:2584)
        at org.apache.catalina.loader.WebappClassLoader.checkThreadLocalMapForLeaks(WebappClassLoader.java:2500)
        at org.apache.catalina.loader.WebappClassLoader.checkThreadLocalsForLeaks(WebappClassLoader.java:2455)
        at org.apache.catalina.loader.WebappClassLoader.clearReferences(WebappClassLoader.java:1996)
        at org.apache.catalina.loader.WebappClassLoader.stop(WebappClassLoader.java:1902)
        at org.apache.catalina.loader.WebappLoader.stopInternal(WebappLoader.java:662)
        at org.apache.catalina.util.LifecycleBase.stop(LifecycleBase.java:232)
        at org.apache.catalina.core.StandardContext.stopInternal(StandardContext.java:5669)
        at org.apache.catalina.util.LifecycleBase.stop(LifecycleBase.java:232)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:160)
        at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
        at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:744)
Started Tomcat Server
The Server is running at http://localhost:8080
core invalid bug jira

Most helpful comment

Rob Winch said:

The problem is with your configuration. There are multiple issues that I see:

  • You should not invoke .build() in configure(AuthenticationManagerBuilder authManagerBuilder) This is the root of this error you are getting.
  • You should not use hasRole("ROLE_ADMIN"), hasRole("ROLE_OWNER"), etc The ROLE_ should be removed as it is implied when you use hasRole method. For example, hasRole("ADMIN")
  • Just as with <intercept-url> each entry under authorizedRequests() is considered in order. So you cannot state .anyRequest().authenticated() and then invoke any other matchers i.e. .regexMatchers("....").hasRole("ROLE_USER") You should instead ensure that anyRequest() is the last method invoked on authorizedRequests()

A summary of these changes (there are some more improvements further down), along with some formatting improvement, can be seen below. You can also find a lot of this documented in the reference http://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/ and in the guides http://docs.spring.io/spring-security/site/docs/3.2.x/guides/

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/", "/home", "greeting").permitAll()
                .regexMatchers("....").hasRole("USER") // no ROLE_
                .antMatchers("...").hasRole("USER")
                .regexMatchers("...").hasRole("OWNER")
                .antMatchers("...").hasRole("OWNER")
                .regexMatchers("...").hasRole("ADMIN")
                .antMatchers("..").hasRole("ADMIN")
                .anyRequest().authenticated() // last method on authorizedRequests()
                .and()
            .formLogin()
                .defaultSuccessUrl("/afterLogin")
                .loginPage("/profiles/lognin/form")
                .failureUrl("/accessDenied");

    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth)
            throws Exception {
         auth
             .authenticationProvider(this.getDaoAuthenticationProvider());
    }

    @Bean
    public AuthenticationManager authMgr() {
        List<AuthenticationProvider> providers = new ArrayList<AuthenticationProvider>();
        providers.add(this.getDaoAuthenticationProvider());
        return new ProviderManager(providers);
    }

    private DaoAuthenticationProvider getDaoAuthenticationProvider() {

        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider
                .setUserDetailsService(getUserDetailsService());
        daoAuthenticationProvider.setPasswordEncoder(new BCryptPasswordEncoder());
        return daoAuthenticationProvider;
    }

    private UserDetailsService getUserDetailsService() {
        return new UserDetailsServiceImpl();
    }
}

There are a number of things you can do to clean this up:

  • Since the rest of the guide uses Spring MVC use @EnableWebMvcSecurity instead of @EnableWebSecurity to ensure you have integration with Spring MVC See http://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/#mvc
  • Use the configureGlobal option for authentication you can make this configuration much more concise. The difference is that global configuration uses @Autowired and will be visible everywhere (i.e. method security and http security can see it).
  • Ensure to grant access to the login page
  • Ensure to grant access to logout resources
@Configuration
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/", "/home", "greeting").permitAll()
                .regexMatchers("....").hasRole("USER")
                .antMatchers("...").hasRole("USER")
                .regexMatchers("...").hasRole("OWNER")
                .antMatchers("...").hasRole("OWNER")
                .regexMatchers("...").hasRole("ADMIN")
                .antMatchers("..").hasRole("ADMIN")
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .defaultSuccessUrl("/afterLogin")
                .loginPage("/profiles/lognin/form")
                .failureUrl("/accessDenied")
                .permitAll() // grant access to login stuff
                .and()
            .logout()
                .permitAll();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth)
            throws Exception {
         auth
             .userDetailsService(userDetailsService())
                 .passwordEncoder(new BCryptPasswordEncoder());
    }

    @Bean
    public UserDetailsServiceImpl userDetailsService() {
        return new UserDetailsServiceImpl();
    }
}

Also not fighting the defaults will remove some additional configuration. I'd recommend going with the following which is much more concise:

@Configuration
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/", "/home", "greeting").permitAll()
                .regexMatchers("....").hasRole("USER")
                .antMatchers("...").hasRole("USER")
                .regexMatchers("...").hasRole("OWNER")
                .antMatchers("...").hasRole("OWNER")
                .regexMatchers("...").hasRole("ADMIN")
                .antMatchers("..").hasRole("ADMIN")
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .permitAll()
                .and()
             .logout()
                 .permitAll();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
         auth
             .userDetailsService(userDetailsService())
                 .passwordEncoder(new BCryptPasswordEncoder());
    }

    @Bean
    public UserDetailsServiceImpl userDetailsService() {
        return new UserDetailsServiceImpl();
    }
}

I'd encourage you to go over the previously mentioned documentation provided with Spring Security. I'd also look at the existing sample applications http://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/#jc

All 10 comments

Rob Winch said:

Please provide the configuration you are using to reproduce this

Vernon said:

My configuration is based on this guide https://spring.io/guides/tutorials/web/6/.

@Order(1)
public class SecurityWebAppInitializer
   extends AbstractSecurityWebApplicationInitializer { }


@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests().antMatchers("/", "/home", "greeting").permitAll()
                .anyRequest().authenticated();
        http.formLogin()
                .defaultSuccessUrl("/afterLogin")
                .loginPage("/profiles/lognin/form")
                .failureUrl("/accessDenied")
                .and()
                .authorizeRequests()
                .regexMatchers("....")
                .hasRole("ROLE_USER")
                .antMatchers("...")
                .hasRole("ROLE_USER")
                .regexMatchers("...")
                .hasRole("ROLE_OWNER")
                .antMatchers("...")
                .hasRole("ROLE_OWNER")
                .regexMatchers("...")
                .hasRole("ROLE_ADMIN")
                .antMatchers("..").hasRole("ROLE_ADMIN");

    }

    @Override
    protected void configure(AuthenticationManagerBuilder authManagerBuilder)
            throws Exception {
         authManagerBuilder.authenticationProvider(this.getDaoAuthenticationProvider()).build();
    }

    @Bean
    public AuthenticationManager authMgr() {
        List<AuthenticationProvider> providers = new ArrayList<AuthenticationProvider>();
        providers.add(this.getDaoAuthenticationProvider());
        return new ProviderManager(providers);
    }

    private DaoAuthenticationProvider getDaoAuthenticationProvider() {

        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider
                .setUserDetailsService(getUserDetailsService());
        daoAuthenticationProvider.setPasswordEncoder(new BCryptPasswordEncoder());
        return daoAuthenticationProvider;
    }

    private UserDetailsService getUserDetailsService() {
        return new UserDetailsServiceImpl();
    }
}

Rob Winch said:

The problem is with your configuration. There are multiple issues that I see:

  • You should not invoke .build() in configure(AuthenticationManagerBuilder authManagerBuilder) This is the root of this error you are getting.
  • You should not use hasRole("ROLE_ADMIN"), hasRole("ROLE_OWNER"), etc The ROLE_ should be removed as it is implied when you use hasRole method. For example, hasRole("ADMIN")
  • Just as with <intercept-url> each entry under authorizedRequests() is considered in order. So you cannot state .anyRequest().authenticated() and then invoke any other matchers i.e. .regexMatchers("....").hasRole("ROLE_USER") You should instead ensure that anyRequest() is the last method invoked on authorizedRequests()

A summary of these changes (there are some more improvements further down), along with some formatting improvement, can be seen below. You can also find a lot of this documented in the reference http://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/ and in the guides http://docs.spring.io/spring-security/site/docs/3.2.x/guides/

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/", "/home", "greeting").permitAll()
                .regexMatchers("....").hasRole("USER") // no ROLE_
                .antMatchers("...").hasRole("USER")
                .regexMatchers("...").hasRole("OWNER")
                .antMatchers("...").hasRole("OWNER")
                .regexMatchers("...").hasRole("ADMIN")
                .antMatchers("..").hasRole("ADMIN")
                .anyRequest().authenticated() // last method on authorizedRequests()
                .and()
            .formLogin()
                .defaultSuccessUrl("/afterLogin")
                .loginPage("/profiles/lognin/form")
                .failureUrl("/accessDenied");

    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth)
            throws Exception {
         auth
             .authenticationProvider(this.getDaoAuthenticationProvider());
    }

    @Bean
    public AuthenticationManager authMgr() {
        List<AuthenticationProvider> providers = new ArrayList<AuthenticationProvider>();
        providers.add(this.getDaoAuthenticationProvider());
        return new ProviderManager(providers);
    }

    private DaoAuthenticationProvider getDaoAuthenticationProvider() {

        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider
                .setUserDetailsService(getUserDetailsService());
        daoAuthenticationProvider.setPasswordEncoder(new BCryptPasswordEncoder());
        return daoAuthenticationProvider;
    }

    private UserDetailsService getUserDetailsService() {
        return new UserDetailsServiceImpl();
    }
}

There are a number of things you can do to clean this up:

  • Since the rest of the guide uses Spring MVC use @EnableWebMvcSecurity instead of @EnableWebSecurity to ensure you have integration with Spring MVC See http://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/#mvc
  • Use the configureGlobal option for authentication you can make this configuration much more concise. The difference is that global configuration uses @Autowired and will be visible everywhere (i.e. method security and http security can see it).
  • Ensure to grant access to the login page
  • Ensure to grant access to logout resources
@Configuration
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/", "/home", "greeting").permitAll()
                .regexMatchers("....").hasRole("USER")
                .antMatchers("...").hasRole("USER")
                .regexMatchers("...").hasRole("OWNER")
                .antMatchers("...").hasRole("OWNER")
                .regexMatchers("...").hasRole("ADMIN")
                .antMatchers("..").hasRole("ADMIN")
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .defaultSuccessUrl("/afterLogin")
                .loginPage("/profiles/lognin/form")
                .failureUrl("/accessDenied")
                .permitAll() // grant access to login stuff
                .and()
            .logout()
                .permitAll();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth)
            throws Exception {
         auth
             .userDetailsService(userDetailsService())
                 .passwordEncoder(new BCryptPasswordEncoder());
    }

    @Bean
    public UserDetailsServiceImpl userDetailsService() {
        return new UserDetailsServiceImpl();
    }
}

Also not fighting the defaults will remove some additional configuration. I'd recommend going with the following which is much more concise:

@Configuration
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/", "/home", "greeting").permitAll()
                .regexMatchers("....").hasRole("USER")
                .antMatchers("...").hasRole("USER")
                .regexMatchers("...").hasRole("OWNER")
                .antMatchers("...").hasRole("OWNER")
                .regexMatchers("...").hasRole("ADMIN")
                .antMatchers("..").hasRole("ADMIN")
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .permitAll()
                .and()
             .logout()
                 .permitAll();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
         auth
             .userDetailsService(userDetailsService())
                 .passwordEncoder(new BCryptPasswordEncoder());
    }

    @Bean
    public UserDetailsServiceImpl userDetailsService() {
        return new UserDetailsServiceImpl();
    }
}

I'd encourage you to go over the previously mentioned documentation provided with Spring Security. I'd also look at the existing sample applications http://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/#jc

Rob Winch said:

As this is a configuration error, this is being marked as invalid

Vernon said:

The configuration works find so far. I run into a log in problem if I don't use the default login form. With a custom login form, I get the following error after clicking the submit button.

HTTP Status 405 - Request method 'POST' not supported

type Status report

message Request method 'POST' not supported

description The specified HTTP method is not allowed for the requested resource.

I can't see any different between the html code of the default one and my custom one. Here is my login form (from the source on a browser generated by Thymeleaf):

form class="form-horizontal" role="form" method="POST" action="/login">  

    <div class="form-group">
        <label for="j_username" class="col-sm-2 control-label">username</label>
        <div class="col-sm-10">
            <input type="text" class="form-control" name="username" id="username" placeholder="Username" autofocus="autofocus" />
        </div>
    </div>
    <div class="form-group">
        <label for="j_password" class="col-sm-2 control-label">password</label>
        <div class="col-sm-10">
            <input type="password" class="form-control" name="password" placeholder="Password" id="password" />
        </div>
    </div>
    <div class="form-group">
        <div class="col-sm-offset-2 col-sm-10">
            <div class="checkbox">
                <label for="rememberMe">
                 <input type="checkbox" name="_spring_security_remember_me">rememberme</input>
                </label>
            </div>
        </div>
    </div>
    <div class="form-group">
        <div class="col-sm-offset-2 col-sm-10">
            <button type="submit" class="btn btn-default">submit</button>
        </div>
    </div>
    <div class="form-group">
        <a href="/profiles/usernameRetrieval">forgetYourUsernameOrPassword</a>
    </div>
<input type="hidden" name="_csrf" value="120af6e9-da06-43b7-81a0-ea85cac3f5e0" />
</form> 

Rob Winch said:

It appears you are submitting the incorrect URL, so Spring Security is ignoring it & your application is ignoring it. With your current configuration you should POST to loginPage("/profiles/lognin/form") You can also specify loginProcessingUrl if you would like to change it.

Vernon said:

I see and that works. So, the way of login form configuration isn't the same as the previous version of SS.

Rob Winch said:

Java Configuration was first finalized in Spring Security 3.2.0 so it has always been this way (unless you count the milestones leading up to it which non-passive changes were made). If you mean is it the same as XML, no it is not. Java Configuration tries to have better defaults that XML cannot make due to passivity concerns. In Spring Security 4.0 XML will be updated to have these defaults too.

Vernon said:

I also run into a backward incompatible problem on the logout mechanism. For those non-backward compatible items, the document should have a section of migration notices from a previous version to the 3.2 version. Just a suggestion.

Rob Winch said:

vernon Do you mean differences between the defaults of XML configuration vs Java Configuration? If so, I agree there certainly needs to be a migration guide. Feel free to watch and/or vote for SEC-2496 If this is not what you mean, can you please elaborate?

Was this page helpful?
0 / 5 - 0 ratings