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
YouTube version
Managing Users
- So we know that we implement the
UserDetailsinterface to describe users such that Spring Security understands them. But how does Spring Security manage them? Well that is done with theUserDetailsServiceinterface.
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 calledusername(considered to be unique). The object that is returned by this method is an instance of theUserDetailsinterface and if the user is not found, then the method throws a UsernameNotFoundException exception. - So now we can create a new class, call it
CustomUserDetailsServiceand have it implement theUserDetailsService.
public class CustomUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return null;
}
}
- 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);
}
- 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 theRepositoryinterface . Which really means that it is a premade interface that gives us methods and handles the bean registration for us.UsersandLongare used for the predefined methods thatJpaRepositorygives us.
@Query
- With this annotation and the
nativeQuery = truewe have created aNative 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
-
SELECTis used to signify that we are about to execute a query on a database table. the*is used to mean all, soSELECT *should be read asSELECT ALL.FROMis used to indicate which table is being queried.WHEREis used to indicate a conditional filter andUsers.username = ?1is the filter. The?1is 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
CustomUserDetailsServiceclass 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;
}
@Autowired
- Notice this annotation above the userRepository field. Any field annotated with this is
injectedright after construction of the bean(more on beans later). What I means when I sayinjectedis 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 ofUserRepositorywill get placed in this field. So that means that as long as userRepository is annotated with@Autowiredwe 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
SecurityUserand 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
ProjectConfigand annotate it with@Configuration. This annotation means that this class is responsible for creating beans in theInversion 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
UserDetailsServicewe 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();
}
}
- The
@Beanannotation tells spring to call this method and turn its returned object into a bean. Since the return type isUserDetailsServiceand thanks to the power of polymorphism Spring Security will use ourCustomUserDetailsServiceinstance for futureUserDetailsServiceuse.
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.
Top comments (0)