Advanced Registration and Authentication with Spring Boot + Spring Security + Spring Session (Redis) Pt 1
Building Spring Boot application with full registration, using Spring Security for authentication & authorization functionality and Spring Session for of course session management with Redis as session store.
Prerequisites
- JDK 8+
- PostgreSQL (or any other relational DB, which is supported by JPA)
- Redis
Project Initialization.
Spring Boot project can be easily created with Spring Initialzr . I hope all of you would understand its interface and could manage the creation of project. Then after pressing generate button, just unzip project in the desirable directory and open with you favorite IDE.
Below I attach the pom.xml file of the generated project. (If you don’t use PostgreSQL, then you may find driver for your DB in maven repository)
Some Theory
I don’t want to copy paste theoretical stuff from other recourse as you can find it yourself, instead I will explain how I understand it.
Registration is actually just process of creating your account/profile.
Authentication is a process of checking if you are existing and valid user or not, in other words, that is the question “Who are you?”.
Simply it is the process when you send you credentials (login and password) and server validates them. When you see something like “Bad credentials” that means that you failed Authentication. Vice versa, when you successfully logged in, thus you passed Authentication. For some very simple services it can be enough. But as we are in Java world, actually almost all the time we will have something more complicated than just verifying the user. Most of the time we will need segregate users by the permissions to actions that can be done in our service. For this purpose we have Authorization.
Authorization is a process of determining of the permissions of the already authenticated user, in other words, that is the question “What are you permitted to do?”
Well, but we have just sent our credentials once, then how does server remember us for a long time? I guess, you are reading it, not right after login, most probably you even forgot when you logged in last time (of course if you do not clear browser cache and history all the time). But how does it happen?
Firstly, I need to mention, that it is not always true that server remembers us. We have two possible cases : Statefull and Stateless authentication & authorization system.
Stateless means that we remind who we are, within each request. Main implementation is JWT, but it is out of the current topic.
Statefull means that server remembers us. Most famous implementation is session.
Session is just data, which is stored in server and has some identifier (id). This id then is sent within each request and allows server to retrieve details of current user (commonly called principal) and then process them.
Remark 1 : do not confuse session id and JWT. Session id is just simple identifier and all data still is stored in the serve, whereas JWT is self-contained, and all user’s details are encoded in it, thus server does not store any data of currently logged in user.
Remark 2 : nowadays everyone develops REST APIs, and if you google security for REST APIs, most of the articles and answers will tell you that REST API must be stateless and thus we should never use sessions, and JWT is the main option. But, first of all, REST is just principles and nobody can forbid us to use just some of them regarding our needs. The main disadvantage of common sessions is that they are stored in the RAM of the server and if server downs, then we lose our sessions, and also it used to be very hard to use session in the microservices (but right now Spring Session totally solves this problem). While JWT seems as very logical and handy thing, but it has advantage only in short-lived scenarios. When we need to be authenticated just during some minutes. But now imagine social network, where you want to be authenticated during long period. Somebody can tell “what stops you from just setting expiration time for JWT of 1 year or even infinity?”. Ok, let me give you an example. Some bad person entered your account, let’s say by guessing or stealing your credentials. Then you get notification that “some person from somewhere visits your account from some device” and you realize that your account was hacked. You first action would be changing the password. With session, that is okay, server remembers all you session, and after you changed your password, it just removes (expires) all existing sessions and thus all devices that was logged in become logged out. Then you just sign in entering your newly created password and continue using this social network. While with JWT, it will be valid till the expiration. Then imagine it has expiration for 1 year or even worse, does not have expiration, then you account is totally insecure. Some people check the JWT against the DB per each request, but then tell me, what is the difference between JWT and Sessions in this case, right, no difference. And let me say more, checking JWT each time against the DB totally violates the idea of JWT.
Spring Implementations
Of course Spring as any full-fledged framework, have all ready to use implementations of the above terms.
The main interface that represents our user is UserDetails.
The main interface for defining user’s permission is GrantedAuthority.
The main interface that is used by authentication mechanisms for retrieving user is UserDetailsService
For configuring mentioned above authentication mechanisms we just need to override some methods of WebSecurityConfigurerAdapter abstract class
Let’s start with Role that will be implementation of GrantedAuthority, because then User (implementation of UserDetails) will have relation to it.
Actually Role is particular case of GrantedAuthority. Generally authority is represented something like “resource_permission” e.g “articles_write”, “users_update”, “comments_read” and etc.
But Role helps us to group permissions not by resources, but by role (position) of the user in our application. Because it is obvious that ADMIN has all privileges, whereas simple USER have minimum of privileges. So we will use this way, but maybe later I will try to make more detailed article about Authorities where I will cover this in more details.
Then DAO for Role
IMPORTANT !!!
Below you see the DAO for User, and actually Spring Data JPA allows us just create method findByEmail(String email) and it would be enough for retrieving User from DB, but why then I write custom query with Query annotation?
Let me explain. Spring Security requires that when we load our User for Authentication, it has to have Roles within it. So when we retrieve User, we also should retrieve its Roles. We have multiple options for this. One of them is setting fetch type eager of ManyToMany annotation in the User entity. So it would be like that
@ManyToMany(fetch = FetchType.EAGER)
private Set<Role> roles;
But it will lead to the problem, that all the time when we retrieve user, it will also retrieve roles and this causes performance overhead. So what I suggest, is using perfect JPA query keyword which FETCH. It allow us to omit writing joins ourselves and do all the work for us. And also I add the suffix “withRoles” to the method name, as maybe later, we will need finding User by email ,but without roles.
I want to remind that it is my first article, and then I need your opinions. Second part will be published after I will get some opinion from you. Thank you very much for reading.