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
UserDetails
interface to describe users such that Spring Security understands them. But how does Spring Security manage them? Well that is done with theUserDetailsService
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 calledusername
(considered to be unique). The object that is returned by this method is an instance of theUserDetails
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 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 theRepository
interface . Which really means that it is a premade interface that gives us methods and handles the bean registration for us.Users
andLong
are used for the predefined methods thatJpaRepository
gives us.
@Query
- With this annotation and the
nativeQuery = true
we 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
-
SELECT
is 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
.FROM
is used to indicate which table is being queried.WHERE
is used to indicate a conditional filter andUsers.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;
}
@Autowired
- Notice this annotation above the userRepository field. Any field annotated with this is
injected
right after construction of the bean(more on beans later). What I means when I sayinjected
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 ofUserRepository
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 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
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();
}
}
- The
@Bean
annotation tells spring to call this method and turn its returned object into a bean. Since the return type isUserDetailsService
and thanks to the power of polymorphism Spring Security will use ourCustomUserDetailsService
instance for futureUserDetailsService
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.
Top comments (0)