I have a spring boot webapp that uses Java configuration to configure JdbcUserDetailsManager:
@Configuration @EnableWebMvcSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired protected DataSource dataSource; @Autowired public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception { auth.jdbcAuthentication() .dataSource(dataSource) .usersByUsernameQuery("select username as principal, password as credentials, true from users where username = ?") .authoritiesByUsernameQuery("select username as principal, authority as role from authorities where username = ?") .rolePrefix("ROLE_"); } @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .authorizeRequests() .antMatchers("/api/**") .authenticated() .and() .formLogin() .successHandler( (request, response, authentication) -> { response.setStatus(HttpStatus.NO_CONTENT.value()); }) .failureHandler( (request, response, authentication) -> { response.setStatus(HttpStatus.FORBIDDEN.value()); }) .and() .logout() .logoutUrl("/logout") .logoutSuccessHandler( (request, response, authentication) -> { response.setStatus(HttpStatus.NO_CONTENT.value()); }); } }
I can set a breakpoint in configAuthentication() , so I know the method is called. Now I want to get the JdbcUserDetailsManager introduced in my Application class:
@EnableAutoConfiguration @ComponentScan public class Application { private Environment env; private UserDetailsManager userDetailsManager; @Autowired public Application(JdbcTemplate jdbcTemplate, Environment env, UserDetailsManager userDetailsManager) { this.env = env; this.userDetailsManager = userDetailsManager; ...
When I try to run the application, I get the following error:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'application': Unsatisfied dependency expressed through constructor argument with index 2 of type [org.springframework.security.provisioning.UserDetailsManager]: : No qualifying bean of type [org.springframework.security.provisioning.UserDetailsManager] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.provisioning.UserDetailsManager] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
But I know that JdbcUserDetailsManager gets an instance before calling the Application constructor. What's going on here? How can I verify that the JdbcUserDetailsManager is actually registered in context?
Update: By changing my SecurityConfig as follows, I was able to solve the problem:
@Configuration @EnableWebMvcSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired protected DataSource dataSource; private JdbcUserDetailsManager userDetailsManager; @Autowired public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception { this.userDetailsManager = auth.jdbcAuthentication().dataSource(dataSource) .usersByUsernameQuery( "select username,password,enabled from users where username=?") .authoritiesByUsernameQuery( "select username, role from user_roles where username=?").getUserDetailsService(); } @Bean(name = "userDetailsManager") public JdbcUserDetailsManager getUserDetailsManager() { return userDetailsManager; } @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .authorizeRequests() .antMatchers("/api/**") .authenticated() .and() .formLogin() .successHandler( (request, response, authentication) -> { response.setStatus(HttpStatus.NO_CONTENT.value()); }) .failureHandler( (request, response, authentication) -> { response.setStatus(HttpStatus.FORBIDDEN.value()); }) .and() .logout() .logoutUrl("/logout") .logoutSuccessHandler( (request, response, authentication) -> { response.setStatus(HttpStatus.NO_CONTENT.value()); }); } }
Head towards Plinio Pantaleo in order to push me in the right direction. Unfortunately, I cannot reward Bounty for the comment. I also still do not understand why AuthenticationManagerBuilder does not register UserDetailsService as a Bean in context automatically. If someone can give an authoritative answer to the question of why I should provide a getter, or can explain how to make it work without a getter (which seems a bit hacked to me), I am rewarded for this answer.