I have been working on refactoring the Spring Security Calendar application from XML configuration to Java Configuration.
In the migration, the is a DefaultService.java file that is using UserDetailsManager, which extends UserDetailsService:
DefaultService:
@Repository public class DefaultCalendarService implements CalendarService { ... private UserDetailsManager userDetailsManager; @Autowired public DefaultCalendarService(final EventDao eventDao, final CalendarUserDao userDao, final UserDetailsManager userDetailsManager) { ...
UserDetailsManager:
public interface UserDetailsManager extends UserDetailsService { ...
But, during the Autowire sequence, the Security Objects created from the WebSecurityConfigurerAdapter and used in other @Component’s, can’t be found:
*************************** APPLICATION FAILED TO START *************************** Description: Parameter 2 of constructor in com.packtpub.springsecurity.service.DefaultCalendarService required a bean of type 'org.springframework.security.provisioning.UserDetailsManager' that could not be found. Action: Consider defining a bean of type 'org.springframework.security.provisioning.UserDetailsManager' in your configuration.
The crux of the issue is that with Java configuration and @Autowired-driven configuration, the configuration initialization ordering does not allow for Security Objects to be initialized before other @Component’s.
There is a JIRA already discussing this issue, and eluding to a possible fix in Spring Security 5:
https://jira.spring.io/browse/SPR-13779
I created a custom WebSecurityConfigurerAdapter:
@Configuration @EnableWebSecurity(debug = true) public class SecurityConfig extends WebSecurityConfigurerAdapter {
Which has a method we can override to get access to the UserDetailsService as seen here:
@Bean @Override public UserDetailsService userDetailsService() { InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); manager.createUser(User.withUsername("user").password("password").roles("USER").build()); manager.createUser(User.withUsername("admin").password("admin").roles("USER", "ADMIN").build()); manager.createUser(User.withUsername("user1@example.com").password("user1").roles("USER").build()); manager.createUser(User.withUsername("admin1@example.com").password("admin1").roles("USER", "ADMIN").build()); return manager; }
The issue is that we actually needed a UserDetailsManager not a UserDetailsService, so I changed the return type to UserDetailsManager as seen:
@Bean @Override public UserDetailsManager userDetailsService() { ...
Now we can successfully Autowire a UserDetailsManager into other @Components in our IoC.
Recent Comments