Security Top

I just announced the new Learn Spring Security course, including the full material focused on the new OAuth2 stack in Spring Security 5:

>> CHECK OUT THE COURSE

1. Overview

In this article, we will show how to create a custom database-backed UserDetailsService for authentication with Spring Security.

2. UserDetailsService

The UserDetailsService interface is used to retrieve user-related data. It has one method named loadUserByUsername() which can be overridden to customize the process of finding the user.

It is used by the DaoAuthenticationProvider to load details about the user during authentication.

3. The User Model

For storing users, we will create a User entity that is mapped to a database table, with the following attributes:

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(nullable = false, unique = true)
    private String username;

    private String password;

    // standard getters and setters
}

4. Retrieving a User

For the purpose of retrieving a user associated with a username, we will create a DAO class using Spring Data by extending the JpaRepository interface:

public interface UserRepository extends JpaRepository<User, Long> {

    User findByUsername(String username);
}

5. The UserDetailsService

In order to provide our own user service, we will need to implement the UserDetailsService interface.

We'll create a class called MyUserDetailsService that overrides the method loadUserByUsername() of the interface.

In this method, we retrieve the User object using the DAO, and if it exists, wrap it into a MyUserPrincipal object, which implements UserDetails, and returns it:

@Service
public class MyUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) {
        User user = userRepository.findByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException(username);
        }
        return new MyUserPrincipal(user);
    }
}

Let's define the MyUserPrincipal class as follows:

public class MyUserPrincipal implements UserDetails {
    private User user;

    public MyUserPrincipal(User user) {
        this.user = user;
    }
    //...
}

6. Spring Configuration

We will demonstrate both types of Spring configurations: XML and annotation-based, which are necessary in order to use our custom UserDetailsService implementation.

6.1. Annotation Configuration

All we need to do to enable our custom UserDetailsService is add it to our application context as a bean.

Since we configured our class with the @Service annotation, the application will automatically detect it during component-scan, and it will create a bean out of this class. Therefore, there isn't anything else we need to do here.

Alternatively, we can:

  • configure it in the authenticationManager using the AuthenticationManagerBuilder#userDetailsService method
  • set it as a property in a custom authenticationProvider bean, and then inject that using the AuthenticationManagerBuilder# authenticationProvider function

6.2. XML Configuration

On the other hand, for the XML configuration we need to define a bean with type MyUserDetailsService, and inject it into Spring's authentication-provider bean:

<bean id="myUserDetailsService" 
  class="org.baeldung.security.MyUserDetailsService"/>

<security:authentication-manager>
    <security:authentication-provider 
      user-service-ref="myUserDetailsService" >
        <security:password-encoder ref="passwordEncoder">
        </security:password-encoder>
    </security:authentication-provider>
</security:authentication-manager>
    
<bean id="passwordEncoder" 
  class="org.springframework.security
  .crypto.bcrypt.BCryptPasswordEncoder">
    <constructor-arg value="11"/>
</bean>

7. Other Database-backed Authentication Options

The AuthenticationManagerBuilder offers one other method to configure JDBC-based authentication in our application.

We'll have to configure the AuthenticationManagerBuilder.jdbcAuthentication with a DataSource instance. If our database follows the Spring User Schema, then the default configurations will suit us well.

We have seen a basic configuration using this approach in a previous post.

The JdbcUserDetailsManager entity resulting from this configuration implements the UserDetailsService too.

As a result, we can conclude that this configuration is easier to implement, especially if we're using Spring Boot that automatically configures the DataSource for us.

If we need, anyway, a higher level of flexibility, customizing exactly how the application will fetch the user details, then we'll opt for the approach we followed in this tutorial.

8. Conclusion

To sum up, in this article we've shown how to create a custom Spring-based UserDetailsService backed by persistent data.

The implementation can be found in the GitHub project – this is a Maven based project, so it should be easy to import and run as it is.

Security bottom

I just announced the new Learn Spring Security course, including the full material focused on the new OAuth2 stack in Spring Security 5:

>> CHECK OUT THE COURSE
8 Comments
Oldest
Newest
Inline Feedbacks
View all comments
Comments are closed on this article!