DEV Community

Cover image for Spring Security. Managing users
Tristan Elliott
Tristan Elliott

Posted on

Spring Security. Managing users

Introduction

  • This series is going to be on Spring Security. This tutorial assumes that you already have your spring project set up, if you do not, please check out the YouTube video HERE

GitHub

HERE

YouTube version

HERE

Managing Users

  • So we know that we implement the UserDetails interface to describe users such that Spring Security understands them. But how does Spring Security manage them? Well that is done with the UserDetailsService interface.

UserDetailsService

  • This interface only contains one method, which is loadUserByUsername(). This method is called by Spring Security whenever any time an object needs to be authenticated. This method takes in a String called username(considered to be unique). The object that is returned by this method is an instance of the UserDetails interface and if the user is not found, then the method throws a UsernameNotFoundException exception.
  • So now we can create a new class, call it CustomUserDetailsService and have it implement the UserDetailsService.
public class CustomUserDetailsService implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return null;
    }
}

Enter fullscreen mode Exit fullscreen mode
  • Since we are making our own UserDetailsService, it has to search for a user and that means talking to our database. Talking to a database is done through a Repository.

Repository

  • The whole goal of a repository in Spring is to significantly reduce the amount of boilerplate code required to implement a data access layer( a Java class created for talking to a database). So we can create a new interface and call it UserRepository, generally you should create a repository for each model object in your database. Our repository will look like this:
public interface UserRepository extends JpaRepository<Users, Long> {

    @Query(value = "SELECT * FROM Users WHERE Users.username = ?1",nativeQuery = true)
    public Users findUserByUserName(String username);
}
Enter fullscreen mode Exit fullscreen mode
  • From the code above your first question probably is, Why an interface, can't we just use a class? And my honest answer is, an interface is simpler. An interface allows us to use the predefined methods Sprig gives us and we don't have to register it as a bean(more on beans later). The next question being, What is the JpaRepository, well it is an extension from the Repository interface . Which really means that it is a premade interface that gives us methods and handles the bean registration for us. Users and Long are used for the predefined methods that JpaRepository gives us.

@Query

  • With this annotation and the nativeQuery = true we have created a Native Query. Which means we create Java methods and have them run SQL statements when they are called.

SQL

  • So lets break down this SQL statement down a little

SELECT * FROM Users WHERE Users.username = ?1

  • SELECT is used to signify that we are about to execute a query on a database table. the * is used to mean all, so SELECT * should be read as SELECT ALL. FROM is used to indicate which table is being queried. WHERE is used to indicate a conditional filter and Users.username = ?1 is the filter. The ?1 is how we specify to use the username parameter in the findUserByUserName method

Implementing the logic

  • Now that we have the repository implemented we can move back to the CustomUserDetailsService class and implement the logic for the loadUserByUsername. The full implementation looks like this:
 @Autowired
    private UserRepository userRepository;


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Users returnedUser = userRepository.findUserByUserName(username);
        if(returnedUser == null){
            throw new UsernameNotFoundException("USERNAME NOT FOUND");
        }
        UserDetails securityUser = new SecurityUser(returnedUser);
        return securityUser;
    }
Enter fullscreen mode Exit fullscreen mode

@Autowired

  • Notice this annotation above the userRepository field. Any field annotated with this isinjected right after construction of the bean(more on beans later). What I means when I say injected is dependency injection and if you are unfamiliar with dependency injection then just give it a quick google. But basically know that when this class gets instantiated by Spring(covered in our bean section) an instance of UserRepository will get placed in this field. So that means that as long as userRepository is annotated with @Autowired we can treat it just like a normal instance of the class.
  • For the actual logic of the code, its pretty straight forward. The only thing that might seem confusing is the SecurityUser and That was a class created in the part one of this tutorial. HERE is the GitHub for the code.

Registering CustomUserDetailsService as a Bean

  • So first lets create a class called ProjectConfig and annotate it with @Configuration. This annotation means that this class is responsible for creating beans in the Inversion of Control(IOC) container. The IOC container is basically a part of the Spring that is responsible for creating and maintaining the life cycle of all the objects in our app.

Beans?

  • Finally we get to talk about beans, What is a bean? They are the objects that form the backbone of our application and are managed by the Spring IOC container. So a bean is really just an object that is instantiated, assembled and managed by the IOC container. Now if we want to go into specifics the part of the IOC that is responsible for managing the beans is called the BeanFactory.

Registering a Bean

  • Now in order for us to use our UserDetailsService we have to register it with the IOC container(turn it into a bean). To do that we do this:
@Configuration
public class projectConfig {
    @Bean
    public UserDetailsService userDetailsService(){
        return new CustomUserDetailsService();
    }
}
Enter fullscreen mode Exit fullscreen mode
  • The @Bean annotation tells spring to call this method and turn its returned object into a bean. Since the return type is UserDetailsService and thanks to the power of polymorphism Spring Security will use our CustomUserDetailsService instance for future UserDetailsService use.

Conclusion

  • Thank you for taking the time out of your day to read this blog post of mine. If you have any questions or concerns please comment below or reach out to me on Twitter.

Discussion (0)