numerical25
numerical25

Reputation: 10790

Spring Boot. A Bean Depends on a Service

Below I have a customUserDetailsService property, and a tokenAuthenticationService property. I need to pass customUserDetailsService into tokenAuthenticationService but tokenAuthenticationService is a @Bean file and customUserDetailsService is a @Service which means that tokenAuthenticationService gets called first with parameter for UserDetailsService as null for the UserDetailsService parameter. I need to either delay the initiation of tokenAuthenticationService as a Bean or Turn tokenAuthenticationService into a service as well and some how pass those parameters as a constructor. How do I go about doing this ?

  package app.config;

    import app.repo.User.CustomUserDetailsService;
    import app.security.*;
    import app.security.filters.StatelessAuthenticationFilter;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.DependsOn;
    import org.springframework.core.annotation.Order;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

    import javax.sql.DataSource;

    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(securedEnabled = true)
    @Order(2)
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

        private static PasswordEncoder encoder;

        @Autowired
        private TokenAuthenticationService tokenAuthenticationService;

        @Autowired
        private UserDetailsService customUserDetailsService;

        @Autowired
        private RESTAuthenticationEntryPoint authenticationEntryPoint;
        @Autowired
        private RESTAuthenticationFailureHandler authenticationFailureHandler;
        @Autowired
        private RESTAuthenticationSuccessHandler authenticationSuccessHandler;

        public WebSecurityConfig() {
            super(true);
        }

        @Autowired
        public void configureAuth(AuthenticationManagerBuilder auth,DataSource dataSource) throws Exception {
            auth.jdbcAuthentication().dataSource(dataSource);
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests().antMatchers("/**").authenticated();
            http.csrf().disable();
            http.httpBasic();
            http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
            http.formLogin().defaultSuccessUrl("/").successHandler(authenticationSuccessHandler);
            http.formLogin().failureHandler(authenticationFailureHandler);
            http.addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService),
                    UsernamePasswordAuthenticationFilter.class);

        }

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(customUserDetailsService);
        }

        @Bean
        public TokenAuthenticationService tokenAuthenticationService() {
            tokenAuthenticationService = new TokenAuthenticationService("tooManySecrets", customUserDetailsService);
            return tokenAuthenticationService;
        }
    }

Upvotes: 0

Views: 3421

Answers (2)

dunni
dunni

Reputation: 44555

You can define the userDetailsService as direct dependency of the TokenAuthenticationService like this:

@Bean
public TokenAuthenticationService tokenAuthenticationService(UserDetailsService userDetailsService) {
    tokenAuthenticationService = new TokenAuthenticationService("tooManySecrets", userDetailsService);
    return tokenAuthenticationService;
}

That way, Spring will make sure, that the UserDetailsService is instantiated and injected when the TokenAuthenticationService is created.

Upvotes: 1

6ton
6ton

Reputation: 4214

You can try annotating tokenAuthenticationService() with @Lazy. Though even if that worked its a bit unpredictable, and future modifications to this or related beans may leave you wondering on why it stopped working.

Best to declare TokenAuthenticationService as @Service & inject UserDetailsService in it.

As a side note its better to not mix @Configuration with application code to avoid these kind of issues.

Update - I don't think @Lazy is going to work here. Since you are relying on @Bean being invoked in the middle of @Autowired beans being processed.

In order for your code to work the @Autowired customUserDetailsService should be set first, then @Bean method called and then @Autowired tokenAuthenticationService should be set.

Upvotes: 0

Related Questions