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.

Mick Knutson

Java, JavaEE, J2EE, WebLogic, WebSphere, JBoss, Tomcat, Oracle, Spring, Maven, Architecture, Design, Mentoring, Instructor and Agile Consulting. http://www.baselogic.com/blog/resume

View all posts

Categories

BLiNC Adsense