Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Again... method security with spring boot/security: Error creating bean with name 'methodSecurityInterceptor' "This object has already been built"

I want to implement method security.

I'm facing a problem with @Secured and @PreAuth annotations. Whenever I add any of those to my service interface, I receive an exception like the following. Without them, my app runs just fine.

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'methodSecurityInterceptor' defined in class path resource [org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.class]: Instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public org.aopalliance.intercept.MethodInterceptor org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration.methodSecurityInterceptor() throws java.lang.Exception] threw exception; nested exception is org.springframework.security.config.annotation.AlreadyBuiltException: This object has already been built

It's a REST services application.

Here are some relevant parts of my config. Please let me know if I should add anything else.

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig {
...

    @Autowired
    private DatabaseAuthenticationProvider databaseAuthenticationProvider;
...
    @Configuration
    @Order(1)                                                        
    public static class RestWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
...
        @Override
        protected void configure(HttpSecurity http) throws Exception {
...
            http
                .authorizeRequests()
                    .antMatchers(HttpMethod.GET, "/usuario/*/rfc/*").authenticated()
                    .antMatchers(HttpMethod.GET, "/usuario/*/rfc/*/").authenticated()
                    .antMatchers(HttpMethod.GET, "/usuario/*").authenticated()
                    .antMatchers(HttpMethod.GET, "/usuario/*/").authenticated()
...          
        }
    }

    @SuppressWarnings("unchecked")
    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        return new AuthenticationManagerBuilder(new NopPostProcessor()).authenticationProvider(databaseAuthenticationProvider).build();
    }

    @SuppressWarnings("rawtypes")
    private static class NopPostProcessor implements ObjectPostProcessor {
        @Override
        public Object postProcess(Object object) {
            return object;
        }
    };

    @Bean
    public MessageDigestPasswordEncoder messageDigestPasswordEncoder() {
        return new MessageDigestPasswordEncoder("sha-256");
    }
}

And:

@Service("databaseAuthenticationProvider")
public class DatabaseAuthenticationProvider
    extends AbstractUserDetailsAuthenticationProvider {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private MessageDigestPasswordEncoder messageDigestPasswordEncoder;
...
    @Override
    protected void additionalAuthenticationChecks(UserDetails arg0,
        UsernamePasswordAuthenticationToken arg1)
        throws AuthenticationException {
        return;
    }
    @Override
    protected UserDetails retrieveUser(String username,
        UsernamePasswordAuthenticationToken authentication)
        throws AuthenticationException {
//        logger.debug("Inside retrieveUser");
        String password = (String) authentication.getCredentials();

        String encryptedPassword = "";

        if (StringUtils.hasText(password)) {
            encryptedPassword = messageDigestPasswordEncoder.encodePassword(password, null);
        }

        //UserDetails user = null;
        String expectedPassword = null;
        List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();

        WebAuthenticationDetails wad = null;
        wad = (WebAuthenticationDetails) authentication.getDetails();
...
        return new org.springframework.security.core.userdetails.User(
            username,
            password, 
            true, // enabled 
            true, // account not expired
            true, // credentials not expired 
            true, // account not locked
            authorities);
    }
...
}

According to AlreadyBuiltException documentation:

Thrown when AbstractSecurityBuilder.build() is two or more times.

But I can't find out why spring is doing that.

Any ponters would be greatly appreciated.

Thank you all.

Here I put some more CallStack

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'usuarioServiceImpl' defined in file [C:\Users\ely\Documents\Desarrollo\Spring\workspace-sts-3.5.1.RELEASE\ws-timbrado-backend\target\classes\mx\i4b\timbrado\service\entidades\UsuarioServiceImpl.class]: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'methodSecurityInterceptor' defined in class path resource [org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.class]: Instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public org.aopalliance.intercept.MethodInterceptor org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration.methodSecurityInterceptor() 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.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:547) ~[spring-beans-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475) ~[spring-beans-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304) ~[spring-beans-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228) ~[spring-beans-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300) ~[spring-beans-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195) ~[spring-beans-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1017) ~[spring-beans-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:960) ~[spring-beans-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:858) ~[spring-beans-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:480) ~[spring-beans-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    ... 73 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'methodSecurityInterceptor' defined in class path resource [org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.class]: Instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public org.aopalliance.intercept.MethodInterceptor org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration.methodSecurityInterceptor() 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.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:597) ~[spring-beans-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1094) ~[spring-beans-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:989) ~[spring-beans-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504) ~[spring-beans-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475) ~[spring-beans-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304) ~[spring-beans-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228) ~[spring-beans-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300) ~[spring-beans-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor.getAdvice(MethodSecurityMetadataSourceAdvisor.java:96) ~[spring-security-core-3.2.3.RELEASE.jar:3.2.3.RELEASE]
    at org.springframework.aop.aspectj.AspectJProxyUtils.isAspectJAdvice(AspectJProxyUtils.java:67) ~[spring-aop-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.aop.aspectj.AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(AspectJProxyUtils.java:49) ~[spring-aop-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator.extendAdvisors(AspectJAwareAdvisorAutoProxyCreator.java:97) ~[spring-aop-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findEligibleAdvisors(AbstractAdvisorAutoProxyCreator.java:89) ~[spring-aop-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean(AbstractAdvisorAutoProxyCreator.java:69) ~[spring-aop-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:376) ~[spring-aop-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:339) ~[spring-aop-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:421) ~[spring-beans-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1558) ~[spring-beans-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539) ~[spring-beans-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    ... 82 common frames omitted
Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public org.aopalliance.intercept.MethodInterceptor org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration.methodSecurityInterceptor() 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) ~[spring-beans-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:586) ~[spring-beans-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    ... 101 common frames omitted
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) ~[spring-security-config-3.2.3.RELEASE.jar:3.2.3.RELEASE]
    at org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration.getAuthenticationManager(AuthenticationConfiguration.java:78) ~[spring-security-config-3.2.3.RELEASE.jar:3.2.3.RELEASE]
    at org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration.authenticationManager(GlobalMethodSecurityConfiguration.java:248) ~[spring-security-config-3.2.3.RELEASE.jar:3.2.3.RELEASE]
    at org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration.methodSecurityInterceptor(GlobalMethodSecurityConfiguration.java:119) ~[spring-security-config-3.2.3.RELEASE.jar:3.2.3.RELEASE]
    at org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration$$EnhancerBySpringCGLIB$$d8bcec4.CGLIB$methodSecurityInterceptor$8(<generated>) ~[spring-core-4.0.3.RELEASE.jar:3.2.3.RELEASE]
    at org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration$$EnhancerBySpringCGLIB$$d8bcec4$$FastClassBySpringCGLIB$$42ba89ef.invoke(<generated>) ~[spring-core-4.0.3.RELEASE.jar:3.2.3.RELEASE]
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228) ~[spring-core-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:312) ~[spring-context-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration$$EnhancerBySpringCGLIB$$d8bcec4.methodSecurityInterceptor(<generated>) ~[spring-core-4.0.3.RELEASE.jar:3.2.3.RELEASE]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.7.0_25]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[na:1.7.0_25]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.7.0_25]
    at java.lang.reflect.Method.invoke(Method.java:606) ~[na:1.7.0_25]
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:166) ~[spring-beans-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    ... 102 common frames omitted  




////// Update 1 //////

Based on @M.Dainum I removed my authenticationManager.

Tried to define my databaseAuthenticationProvider in three ways (included in this post), but all resulted in the same NullPointerException upon authentication.

java.lang.NullPointerException: null
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:147) ~[spring-security-core-3.2.3.RELEASE.jar:3.2.3.RELEASE]

ProviderManager.java:147:

        if (!provider.supports(toTest)) {
- I don't think `provider` is null since I made sure my autowired `databaseAuthenticationProvider` wasn't.
- `toTest` comes from `authentication.getClass()`. It shouldn't be null. Right? (Not sure if I remotely understood the problem)


getProviders() contins:

[null, org.springframework.security.authentication.AnonymousAuthenticationProvider@490adcdf]

Made a mistake before: The provider is null. So the "Autowiriring" is not happenning.

I think this is because in order to use something like: auth.authenticationProvider(databaseAuthenticationProvider);, databaseAuthenticationProvider should be static and that prevents the autowiring. Right?

If I create it with "new", then afterwards the messageDigestPasswordEncoder is null, and so on.

So... How should I solve this?

Op1:

    @Configuration
    @Order(1)                                                        
    public static class RestWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {

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

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

Op2:

    @Configuration
    @Order(1)                                                        
    public static class RestWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {

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

            http.authenticationProvider(databaseAuthenticationProvider);

Op3:

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig {

    @Autowired
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(databaseAuthenticationProvider);
    }




////// Update 2 //////

To "force" the "Autowiriring", tried to remove @Autowire from the attribute and add it to a setter method like this:

public class WebSecurityConfig {
    private static DatabaseAuthenticationProvider databaseAuthenticationProvider;

    @Autowired
    public void setDatabaseAuthenticationProvider(DatabaseAuthenticationProvider databaseAuthenticationProvider) {
        WebSecurityConfig.databaseAuthenticationProvider = databaseAuthenticationProvider;
    }

    @Configuration
    @Order(1)                                                        
    public static class RestWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            if (databaseAuthenticationProvider == null) {
                System.out.println("************************************************* databaseAuthenticationProvider es null");
            } else {
                System.out.println("************************************************* databaseAuthenticationProvider NO ES NULL");
            }
            auth.authenticationProvider(databaseAuthenticationProvider);
        }

And now: back to squere one:

Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public org.aopalliance.intercept.MethodInterceptor org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration.methodSecurityInterceptor() throws java.lang.Exception] threw exception; nested exception is org.springframework.security.config.annotation.AlreadyBuiltException: This object has already been built

I guess the problem wasnt that I had my own AuthenticationManager Somehwere else is being built twice.

How to find it?

By the way... it still has null value:

************************************************* databaseAuthenticationProvider es null

like image 246
elysch Avatar asked Jul 24 '14 23:07

elysch


People also ask

What is AuthenticationManagerBuilder?

AuthenticationManagerBuilder is a helper class that eases the set up of UserDetailService, AuthenticationProvider, and other dependencies to build an AuthenticationManager. For a global AuthenticationManager, we should define an AuthenticationManager as a bean.

What is the use of WebSecurityConfigurerAdapter in spring boot?

configure. Deprecated. Used by the default implementation of authenticationManager() to attempt to obtain an AuthenticationManager . If overridden, the AuthenticationManagerBuilder should be used to specify the AuthenticationManager .

How do I add security to my spring application?

For adding a Spring Boot Security to your Spring Boot application, we need to add the Spring Boot Starter Security dependency in our build configuration file. Maven users can add the following dependency in the pom. xml file. Gradle users can add the following dependency in the build.


1 Answers

I want to thank M.Deinum. I finally got it working.

It is as simple as this:

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig {

    @Configuration
    @Order(1)                                                        
    public static class RestWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {

        @Autowired
        private  DatabaseAuthenticationProvider databaseAuthenticationProvider;

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

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .authorizeRequests()
                    .antMatchers(HttpMethod.GET, "/usuario/*/rfc/*").authenticated()
...

Now my service method is accessible only by the specified role like this:

@Secured({Sec.R_ADMIN})
Usuario getUsuarioByEmisorAndIdUsuario(Emisor emisor, String idUsuario, boolean consultarRoles) throws AuthorizationException;

Being:

public class Sec {
    public static final String R_ADMIN = "ROLE_ADMIN";
like image 53
elysch Avatar answered Sep 23 '22 08:09

elysch