In this part we will be looking at how to authenticate and authorize users using Spring Boot.

Link to Part 5
Part 7 Now Available: Here

Table of Contents:

  1. What is Authentication and Why Do We Need It?
  2. What is Authorization?
  3. Spring Security Module
  4. How Spring Security Works
  5. Logging In Users
  6. Allowing Only Specific Users

Pre-requisites:

  1. How to create an API using Spring Boot
  2. How to access the database using Spring Boot

What is Authentication and Why Do We Need It?

Authentication is basically making sure that the user is who they say they are. In many cases, we want to make sure that the person who is accessing our website is registered with us and not some random person.

For example, say you go to Instagram. Instagram wants to make sure that only you are registered with them and they ask you to login. This is called authenticating or verifying the users identity.

Authentication can be done by you by storing the user info like username and password or by some third party auth provider. For example, you use Gmail to authenticate the user. All the user info is stored in Google’s servers and not yours.

In this tutorial, we are going to be looking at how you can store and authenticate users by yourselves without using a third party.

What is Authorization?

Authorization is different from authentication. Authorization is verifying a user has the permissions to access a particular resource.

For example, many websites offer premium services. Only paid users can access those premium services.

Spring Security Module:

Instead of us having to write all the logic like verifying the user, creating session and checking the user permissions, Spring provides us with the spring-boot-starter-security module that takes care of all that.

To use it, add the following to your pom.xml file in the dependencies section.

  <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

How Spring Security Works:

Spring Security expects you to provide it two things.

  1. The list of usernames and passwords that are allowed
  2. What roles a user has

You can hardcode this or get these info from a database. Typically, the table schema for this would look something like this

The field names need not be user_id or password or active or role. They can be whatever you want.

What matters is this. The Users table need at least 3 fields. One storing a unique username, other storing a password and another one storing a boolean. This boolean field is used for making sure if this user is allowed to authenticate or not. For example, if one of your users deletes his/her account, you can change this to false and that account will be disabled.

The password field is special. It should start with the name of the encryption technique used. In the above example, notice the [noop] in the password. This tells Spring Boot that no encryption has been used. You can specify other fields like [bcrypt] for bcrypt encrypted passwords. Spring Boot will automatically use bcrypt to verify the incoming passwords with the one store in the database.

When you are registering a user, you need to encrypt their passwords and store it in the database. You can read more about it Here.

The roles table needs each username to be mapped to some set of roles. The roles must start with ROLE_ prefix for it to be valid.

If the user has multiple roles, keep them in separate rows as a good practice.

Create the user and roles entity models in Spring Boot as an exercise. We have already covered this in the previous chapters.

Logging In Users:

Start by creating a Security directory inside your Spring Boot project and a new .java file inside it. For example, APISecurity.java

Add the following code inside it

package com.benmeehan111.demo.Security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.security.provisioning.UserDetailsManager;

import javax.sql.DataSource;

@Configuration
public class APISecurityConfig {

@Bean
public UserDetailsManager userDetailsManager(DataSource dataSource) {
JdbcUserDetailsManager jUserDetailsManager = new JdbcUserDetailsManager(dataSource);

jUserDetailsManager.setUsersByUsernameQuery("SELECT user_id, password, active FROM Users WHERE user_id = ?;");

jUserDetailsManager.setAuthoritiesByUsernameQuery("SELECT user_id, role FROM Roles WHERE user_id = ?;");

return jUserDetailsManager;
}
}

We are just creating a Configuration class and defining a @Bean method inside it that returns the user and role details from the database. We will not worry about the roles for now.

We are using the Spring Security’s built-in JdbcUserDetailsManager to fetch the details from the database. The user_id = ? parameter will be replaced by the username which the user enters in the Login form.

That’s it. Pretty simple right? Now if you start your Spring Boot app, and visit a route, it should ask you to login first.

If you haven’t understood how and why this worked, don’t worry. I’ll explain it in the end.

Allowing Only Specific Users:

Now, let’s get Authorization done. In the same class, add an another @Bean method like this

import org.springframework.http.HttpMethod;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

... other imports


@Configuration
public class APISecurityConfig {

... other beans

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(
configurer -> configurer.requestMatchers(HttpMethod.GET,
"/dashboard")
.hasRole("ADMIN"));

http.httpBasic(Customizer.withDefaults());

return http.build();
}
}

This method returns a SecurityFilterChain bean. Inside the method, we are telling Spring Boot to allow only the users who have ADMIN roles to access the /dashboard route.

Of course, you can customize the roles and routes according to your needs.

Now if you re-start you spring boot server and go to the route with the role enabled, you can see that if you try to login with a user that doesn’t have that role, access will be denied.

What Is Really Happening Here?

At this point, you might be asking… How did all of this work? How did Spring Security come to know about our methods?

Well, if you haven’t understood how Spring works yet, this is the concept that will solidify it for you.

In Java or any OOP language, there is something called as ‘Design Patterns’. It is basically a set of rules that say this is how classes and objects should be designed and created. Spring Boot itself is really just an application of these design patterns.

One of those design patterns is ‘Singleton’. It says that, at any point there can be only one object of a particular class. By default, all objects in Spring are Singleton.

We already learned that if we use the @Bean annotation, Spring takes control of whatever that method returns. This is how it looks now,

All the Security Module expects is two objects: One which implements the UserDetailsManager interface and the other which implements the SecurityFilterChain interface. It doesn’t care about how these objects are created and what else they do.

When it starts, Spring Security asks Spring “Hey can you give me objects that satisfy these interfaces ?”.

Then Spring Boot injects those objects that we created into Spring Security because these two objects satisfy those conditions and also because there are no two of these objects (Singleton).

Here we can really see Dependency Injection and Inversion of Control in action.

Wrapping Up:

That concludes Spring Security. Of course, there are a lot more that this module offers than what I could cover here. But this should suffice you for 80% of the use cases and help you to learn further advanced concepts.

“Life is a tragedy for those who feel, and a comedy for those who think.” ― La Bruyere