Security Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> 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 course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE
newest oldest most voted
Notify of
akuma8
Guest
akuma8

Hi,

Thanks again for all your tutorials. I am trying to implement the same thing but it doesn’t work and I can’t find the reason.Is there a way to enable Spring Security error logging messages? It could help me to debug if Spring Security could send me the cause of the failure.

I added the tag in my xml configuration but nothing is displayed in the Eclipse console.

Thanks again.

Here my project in Github : https://github.com/akuma8/chicowaProject

Grzegorz Piwowarek
Guest
Grzegorz Piwowarek

Could you point us to the place where Spring Security gets configured? I could not manage to find it in the configuration package or in resources directory.

akuma8
Guest
akuma8

The xml configuration is in : /WEB-INF/Spring/configurationSpring-security.xml
The implementation of UserDetailsService is in : src/main/com.chicowa.security.ChicowaUserDetailsService.java
I also tried to add a log4j.xml (/src/main/resources/log4j.xml) in the purpose to display the debug messages sent by Spring Security but nothing works.
Regarding Spring Security authentication the 2 others configurations (in-memory and jdbc) work fine but it’s not enough because I can’t manage blocked accounts and other stuffs offered by the custom UserDetailsService.
All my comments in the project are in French, I’ll try to change them.
Many thanks for your reply and happy new year ?

Eugen Paraschiv
Guest

Quick note – it sounds like you simply need to configure the log level for the relevant package (org.springframework.security). It looks like you’re using Log4J, so it should be simple enough to set that to DEBUG or TRACE (only while you’re working on the problem of course). As soon as you do that you should start seeing the log output.
Hope that helps. Cheers,
Eugen.

akuma8
Guest
akuma8

I tried several configurations but any DEBUG or TRACE message is displayed. I’m definitely wrong maybe.

Eugen Paraschiv
Guest

It sounds like your logging config isn’t working then. My suggestion is – have a look at a working project (any of the Spring Security modules in the tutorials project should be fine – and copy the logging config (and dependencies) from there. With your logging properly configured, you should naturally see these statements in your log.
Cheers,
Eugen.

Alexander Loginov
Guest
Alexander Loginov

Hi, this solution does not work me also. After some investigation and debugging, I found the following issue in Java configuration (xml configuration seems to be correct). You can have two alternatives: 1. set everything related to authProvider in configure method (authenticationProvider with userDetailsService and passwordEncoder) and use methods userDetailsService and encode as getters (not beans), OR 2. set nothing in configure method and use bean authenticationProvider for configuration of authenticationProvider (encode should not be bean, because it is used as getter in authenticationProvider) In your code you explicitly set userDetailsService in configure method, and Spring will not use beans… Read more »

Loredana Crusoveanu
Guest
Loredana Crusoveanu

Hello Alexander. I think the confusion here is due to the fact that the bean injection was not pasted in the article. But in the code this is done like this:


@Autowired
private MyUserDetailsService userDetailsService;

So you can see that Spring will use our custom bean.

You make a good point about the configure method.

We will update the article. Thanks for pointing out the issue.

Comments are closed on this article!