Spring Security - Quick Guide


Advertisements

Spring Security - Form Login with Database

Contents

  • Introduction and Overview
  • Basic components of Spring Security
    • AuthenticationFilter
    • AuthenticationManager
    • AuthenticationProvider
    • UserDetailsService
    • PasswordEncoder
    • Spring Security Context
    • Form Login
    • Login with a Database
    • Login Attempts Limit
  • Getting Started (Practical Guide)

Introduction and Overview

In addition to providing various inbuilt authentication and authorization options, Spring Security allows us to customize our authentication process as much as we want. Starting from a custom login page to our very own customized authentication providers and authentication filters, we can pretty much customize every aspect of the authentication process. We can define our own authentication process which can range from basic authentication using a username and a password to a complex one such as two-factor authentication using tokens and OTP’s. Also, we can use various databases – both relational and non-relational, use various password encoders, lock malicious users out of their accounts, and so on.

Today, we are going to discuss three such customizations, namely – custom form-login, a database provided authentication, and limiting login attempts. Though these are pretty basic use-cases, yet these still will let us have a closer look into Spring Security’s authentication and authorization process. We are also going to set up a registration page through which the users will be able to register themselves with our application.

First of all, let’s take a look at the architecture of Spring Security. It starts with servlet filters. These filters intercept requests, perform operations on them, and then pass the requests on to next filters in the filter chain or request handlers or block them if they do not meet certain conditions. It is during this process that Spring Security can authenticate requests and perform various authentication checks on the requests. It can also prevent unauthenticated or malicious requests from accessing our protected resources by not allowing them to pass through. Thus our application and resources stay protected.

Components of Spring Security Architecture

Components of Spring Security Architecture

The basic components of Spring Security, as we can see in the above diagram are given below. We shall discuss them briefly as we go along. We shall also discuss their roles in the authentication and authorization process.

AuthenticationFilter

This is the filter that intercepts requests and attempts to authenticate it. In Spring Security, it converts the request to an Authentication Object and delegates the authentication to the AuthenticationManager.

AuthenticationManager

It is the main strategy interface for authentication. It uses the lone method authenticate() to authenticate the request. The authenticate() method performs the authentication and returns an Authentication Object on successful authentication or throw an AuthenticationException in case of authentication failure. If the method can’t decide, it will return null. The process of authentication in this process is delegated to the AuthenticationProvider which we will discuss next.

AuthenticationProvider

The AuthenticationManager is implemented by the ProviderManager which delegates the process to one or more AuthenticationProvider instances. Any class implementing the AuthenticationProvider interface must implement the two methods – authenticate() and supports(). First, let us talk about the supports() method. It is used to check if the particular authentication type is supported by our AuthenticationProvider implementation class. If it is supported it returns true or else false. Next, the authenticate() method. Here is where the authentication occurs. If the authentication type is supported, the process of authentication is started. Here is this class can use the loadUserByUsername() method of the UserDetailsService implementation. If the user is not found, it can throw a UsernameNotFoundException.

On the other hand, if the user is found, then the authentication details of the user are used to authenticate the user. For example, in the basic authentication scenario, the password provided by the user may be checked with the password in the database. If they are found to match with each other, it is a success scenario. Then we can return an Authentication object from this method which will be stored in the Security Context, which we will discuss later.

UserDetailsService

It is one of the core interfaces of Spring Security. The authentication of any request mostly depends on the implementation of the UserDetailsService interface. It is most commonly used in database backed authentication to retrieve user data. The data is retrieved with the implementation of the lone loadUserByUsername() method where we can provide our logic to fetch the user details for a user. The method will throw a UsernameNotFoundException if the user is not found.

PasswordEncoder

Until Spring Security 4, the use of PasswordEncoder was optional. The user could store plain text passwords using in-memory authentication. But Spring Security 5 has mandated the use of PasswordEncoder to store passwords. This encodes the user’s password using one its many implementations. The most common of its implementations is the BCryptPasswordEncoder. Also, we can use an instance of the NoOpPasswordEncoder for our development purposes. It will allow passwords to be stored in plain text. But it is not supposed to be used for production or real-world applications.

Spring Security Context

This is where the details of the currently authenticated user are stored on successful authentication. The authentication object is then available throughout the application for the session. So, if we need the username or any other user details, we need to get the SecurityContext first. This is done with the SecurityContextHolder, a helper class, which provides access to the security context. We can use the setAuthentication() and getAuthentication() methods for storing and retrieving the user details respectively.

Moving on, let’s now discuss the three custom implementations we are going to use for our application.

Form Login

When we add Spring Security to an existing Spring application it adds a login form and sets up a dummy user. This is Spring Security in auto-configuration mode. In this mode, it also sets up the default filters, authentication-managers, authentication-providers, and so on. This setup is an in-memory authentication setup. We can override this auto-configuration to set up our own users and authentication process. We can also set up our custom login method like a custom login form. Spring Security only has to made aware of the details of the login form like – the URI of the login form, the login processing URL, etc.. It will then render our login form for the application and carry out the process of authentication along with the other provided configurations or Spring’s own implementation.

This custom form setup will only have to abide by certain rules to be integrated with Spring Security. We need to have a username parameter and a password parameter and the parameter names should be “username” and “password” since those are the default names. In case, we use our own parameter names for these fields in the custom we have to inform Spring Security of those changes using the usernameParameter() and passwordParameter() methods. Similarly, for every change we do to the login form or the form login method, we will have to inform Spring Security of those changes with appropriate methods so that it can integrate them with the authentication process.

Login with a Database

As we discussed, Spring Security automatically provides an in-memory authentication implementation by default. We can override this by authenticating users whose details are stored in a database. In this case, while authenticating a user, we can verify the credentials provided by the user against those in the database for authentication. We can also let new users register in our application and store their credentials in the same database. Also, we can provide methods to change or update their passwords or roles or other data. As a result, this provides us with persistent user data which can be used for longer periods of time.

Login Attempts Limit

To limit login attempts in our application we can use Spring Security’s isAccountNonLocked property. Spring Security’s UserDetails provides us with that property. We can set up an authentication method wherein, if any user or someone else provides incorrect credentials for more than a certain number of times, we can lock their account. Spring Security disables authentication for a locked user even if the user provides correct credentials. This is an in-built feature provided by Spring Security. We can store the number of incorrect login attempts in our database. Then against each incorrect authentication attempt, we can update and check with the database table. When the number of such attempts exceeds a given number, we can lock the user out of their account. Consequently, the user will not be able to log in again until their account is unlocked.

Getting Started (Practical Guide)

Let’s start with our application now. The tools we will be needing for this application are listed below −

  • A Java IDE − preferable STS 4, but Eclipse, IntelliJ Idea or any other IDE will do.

  • MySql Server Community Edition − We need to download and install MySql Community Server in our system. We can go to the official website by clicking here.

  • MySql Workbench − It is a GUI tool that we can use to interact with MySql databases.

Database Setup

Let’s set up the database first. We will use a MySql database instance for this application. MySql Server Community Edition is available for free download and use. We will use MySql Workbench to connect with our MySql Server and create a database called “spring” to use with our application.

Then we will create two tables – users and attempts– to persist our users and login attempts. As mentioned earlier, the details of the users registering with our application will be stored in the users table. The number of login attempts by any user will be stored in the attempts table against his username. This way we can track the attempts and take necessary action.

Let’s take a the look at the SQL to setup our users table and attempts table.

CREATE TABLE users ( 
   username VARCHAR(45) NOT NULL , password VARCHAR(45) NOT NULL , 
   account_non_locked TINYINT NOT NULL DEFAULT 1 , 
   PRIMARY KEY (username)
); 
CREATE TABLE attempts ( 
   id int(45) NOT NULL AUTO_INCREMENT, 
   username varchar(45) NOT NULL, attempts varchar(45) NOT NULL, PRIMARY KEY (id) 
);

We can now add a dummy user to our application.

INSERT INTO users(username,password,account_non_locked) 
VALUES ('user','12345', true);

Project Setup

As usual, we will use the Spring Initializer to setup our project. We are going to create a Maven project, with Spring Boot version 2.3.2. Let’s name our project formlogin(we can choose any name we want) and group id as com.tutorial.spring.security. Furthermore, we will use Java version 8 for this project.

Project Setup

Dependencies

Now, coming to the dependencies, we are going to keep our application as simple as possible for this demo. We will keep our focus on the features we want to explore today. So we will choose the minimum number of dependencies that will help us set up our application and get it up and running quickly. Let’s go through the dependencies −

  • Spring Web − It bundles all dependencies related to web development including Spring MVC, REST, and an embedded Tomcat Server.

  • Spring Security − For the implementation of security features provided by Spring Security.

  • Thymeleaf − A server-side Java template engine for HTML5/XHTML/XML.

  • Spring Data JPA − In addition to using all features defined by JPA specification, Spring Data JPA adds its own features such as the no-code implementation of the repository pattern and the creation of database queries from the method name.

  • Mysql Driver − For the MySQL database driver.

With these five dependencies, we can set up our project now. Let’s click on the generate button. This will download our project as a zip file. We can extract it to a folder of our choice. Then we open the project in our IDE. We will be using Spring Tool Suite 4 for this. example.

Let’s load our project into STS. It will take a little time for our IDE to download the dependencies and validating them. Let’s take a look at our pom.xml file.

pom.xml

<?xml version="1.0" encoding="ISO-8859-1"?> 
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
   https://maven.apache.org/xsd/maven-4.0.0.xsd" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xmlns="http://maven.apache.org/POM/4.0.0"> 
   <modelVersion>4.0.0</modelVersion> 
   <parent> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-parent</artifactId> 
      <version>2.3.1.RELEASE</version> 
      <relativePath/> 
      <!-- lookup parent from repository --> 
   </parent> 
   <groupId>com.tutorial.spring.security</groupId> 
   <artifactId>formlogin</artifactId> 
   <version>0.0.1-SNAPSHOT</version> 
   <name>formlogin</name> 
   <description>Demo project for Spring Boot</description> 
   <properties> <java.version>1.8</java.version> 
   </properties>
   <dependencies> 
      <dependency> 
         <groupId>org.springframework.boot</groupId> 
         <artifactId>spring-boot-starter-data-jpa</artifactId> 
      </dependency> 
      <dependency> 
         <groupId>org.springframework.boot</groupId> 
         <artifactId>spring-boot-starter-security</artifactId> 
      </dependency> 
      <dependency> 
         <groupId>org.springframework.boot</groupId> 
         <artifactId>spring-boot-starter-thymeleaf</artifactId> 
      </dependency> 
      <dependency> 
         <groupId>org.springframework.boot</groupId> 
         <artifactId>spring-boot-starter-web</artifactId> 
      </dependency> 
      <dependency> 
         <groupId>org.springframework.boot</groupId> 
         <artifactId>spring-boot-devtools</artifactId> 
         <scope>runtime<scope> <optional>true</optional>
      </dependency> 
      <dependency> 
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId> 
      <scope>runtime</scope> </dependency> 
      <dependency> 
         <groupId>org.springframework.boot</groupId> 
         <artifactId>spring-boot-starter-test</artifactId> 
         <scope>test</scope> 
         <exclusions> 
         <exclusion> 
         <groupId>org.junit.vintage</groupId> 
         <artifactId>junit-vintage-engine</artifactId> 
         </exclusion> 
         </exclusions> 
      </dependency> 
      <dependency> 
         <groupId>org.springframework.security</groupId> 
         <artifactId>spring-security-test<artifactId> 
         <scope>test</scope> 
      </dependency>
   </dependencies> 
   <build> 
      <plugins> 
         <plugin> 
            <groupId>org.springframework.boot</groupId> 
            <artifactId>spring-boot-maven-plugin</artifactId> 
         </plugin> 
      </plugins> 
   </build> 
</project>

We can see that our project details along with our dependencies are enlisted here.

Data Source

We will configure our data source in the application.properties file. As we will be using our local MySQL DB as the data source, so we provide the url, username, and password of our local DB instance here. We have named our database as “spring”.

spring.datasource.url=jdbc:mysql://localhost:3306/spring 
spring.datasource.username=root 
spring.datasource.password=root

Entities

Let’s create our entities now. We start with the User entity which contains three fields – username, password, and accountNonLocked. This User class also implements the UserDetails interface of Spring Security. This class provides core user information. It is used to store user data which can be later encapsulated into Authentication objects. It is not recommended to implement the interface directly. But for our case, since this is a simple application to demonstrate the login with a database, we have implemented this interface directly here to keep thingssimple. We can implement this interface by using a wrapper class around our User entity.

User.java

package com.tutorial.spring.security.formlogin.model; 

import java.util.Collection; 
import java.util.List;
import javax.persistence.Column; 
import javax.persistence.Entity; 
import javax.persistence.Id; 
import javax.persistence.Table; 
import org.springframework.security.core.GrantedAuthority; 
import org.springframework.security.core.userdetails.UserDetails; 

@Entity 
@Table(name = "users") 
public class User implements UserDetails { 

   /** 
   * 
   */ 
   private static final long serialVersionUID = 1L;

   @Id 
   private String username; 
   private String password; @Column(name = "account_non_locked")
   private boolean accountNonLocked; 

   public User() { 
   } 
   public User(String username, String password, boolean accountNonLocked) { 
      this.username = username; 
      this.password = password; 
      this.accountNonLocked = accountNonLocked; 
   } 
   @Override 
   public Collection< extends GrantedAuthority> getAuthorities() { 
      return List.of(() -> "read"); 
   }
   @Override
   public String getPassword() {    
      return password; 
   } 
   public void setPassword(String password) { 
      this.password = password; 
   } 
   @Override 
   public String getUsername() { 
      return username; 
   } 
   public void setUsername(String username) { 
      this.username = username; 
   } 
   @Override 
   public boolean isAccountNonExpired() { 
      return true; 
   } 
   @Override
   public boolean isAccountNonLocked() { 
      return accountNonLocked; 
   } 
   @Override public boolean isCredentialsNonExpired() { 
      return true; 
   } 
   @Override public boolean isEnabled() { 
   return true; 
   } 
   
   public void setAccountNonLocked(Boolean accountNonLocked) { 
      this.accountNonLocked = accountNonLocked; 
   } 
   public boolean getAccountNonLocked() { 
      return accountNonLocked; 
   } 
}

The accountNonLocked field is to be noted here. Every User in Spring Security has the account unlocked by default. To override that property and to lock the users out of their accounts once they exceed the permissible number of attempts, we shall be using this property. If the user exceeds the number of invalid attempts permissible, we shall use this property to lock him out of his account. Also, during every authentication attempt, we shall be checking this property with the isAccountNonLocked() method along with the credentials to authenticate the user. Any user with a locked account will not be allowed to authenticate into the application.

For the other methods of the UserDetails interface, we can simply provide an implementation that returns true for now as we shall not be exploring these properties for this application.

For the list of authorities for this user, let’s assign him a dummy role for now. We shall not be using this property either for this application.

Attempts.java

Moving on, let’s create our Attempts entity to persist our invalid attempts count. As created in the database, we will have the three fields here – username, an integer named attempts to keep counts of the number of attempts, and an identifier.

package com.tutorial.spring.security.formlogin.model; 

import javax.persistence.Entity; 
import javax.persistence.GeneratedValue; 
import javax.persistence.GenerationType; 
import javax.persistence.Id; 

@Entity 
public class Attempts { 
   @Id 
   @GeneratedValue(strategy = GenerationType.IDENTITY) 
   private int id;
   private String username; 
   private int attempts;
   
   /** 
   * @return the id 
   */ 
   public int getId() { 
      return id; 
   } 
   /** 
   * @param id the id to set 
   */ 
   public void setId(int id) {         
      this.id = id; 
   } 
   /** 
   * @return the username 
   */ 
   public String getUsername() { 
      return username; 
   }
   /** 
   * @param username the username to set 
   */ 
   public void setUsername(String username) { 
      this.username = username; 
   } 
   /** 
   * @return the attempts 
   */ 
   public int getAttempts() { 
      return attempts; 
   } 
   /** 
   * @param attempts the attempts to set 
   */ 
   public void setAttempts(int attempts) { 
      this.attempts = attempts; 
   } 
}

Repositories

We have created the entities, let’s create the repositories to store and retrieve data. We will have two repositories, one for each entity class. For both the repository interfaces, we will extend the JpaRepository which provides us with in-built implementations to save and retrieve data from the database configured in our application.properties file. We can also add our methods or queries here in addition to the provided ones.

UserRepository.java

package com.tutorial.spring.security.formlogin.repository; 

import java.util.Optional; 
import org.springframework.data.jpa.repository.JpaRepository; 
import org.springframework.stereotype.Repository; 
import com.tutorial.spring.security.formlogin.model.User; 

@Repository public interface UserRepository extends JpaRepository<User, String> { 
   Optional<User> findUserByUsername(String username); 
}

As discussed, we have added our method to retrieve a user by username here. This will return our user details including username, password and account locked status.

AttemptsRepository.java

package com.tutorial.spring.security.formlogin.repository; 

import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository; 
import org.springframework.stereotype.Repository; 
import com.tutorial.spring.security.formlogin.model.Attempts; 

@Repository 
public interface AttemptsRepository extends JpaRepository<Attempts, Integer> { 
   Optional<Attempts> findAttemptsByUsername(String username); 
}

Similarly, for the Attempts, in our AttemptsRepository, we have added a custom method findAttemptsByUsername(String username) to get data about user attempts using the username. This will return us an Attempts object with the username and the number of failed authentication attempts the user has made.

Configuration

Since we are going to use a custom login form, we have to override the default configuration of Spring Security. To do this we create our configuration class which extends the WebSecurityConfigurerAdapter class of Spring Security.

package com.tutorial.spring.security.formlogin.config; 

import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 
import org.springframework.security.crypto.password.PasswordEncoder; 
@Configuration 
public class ApplicationConfig extends WebSecurityConfigurerAdapter { 
   @Bean 
   public PasswordEncoder passwordEncoder() { 
      return new BCryptPasswordEncoder(); 
   } 
   @Override 
   protected void configure(HttpSecurity http) throws Exception { 
      http 
      .csrf().disable()
      .authorizeRequests().antMatchers("/register**")
      .permitAll() .anyRequest().authenticated() 
      .and() 
      .formLogin() .loginPage("/login")
      .permitAll() 
      .and() 
      .logout() .invalidateHttpSession(true) 
      .clearAuthentication(true) .permitAll(); 
   }
}

Here we did two things−

  • First, we have specified the implementation of the PasswordEncoder interface that we are going to use. We have used an instance of BCryptPasswordEncoder to encode our passwords for this example. The PasswordEncoder interface has many implementations and we can use any of them. We have chosen BCryptPasswordEncoder here as it the most commonly used implementation. It uses the very strong BCrypt hashing algorithm to encode the passwords. It does so by incorporating a salt to protect against rainbow table attacks. In addition to this, bcrypt is an adaptive function: over time, the iteration count can be increased to make it slower, so it remains resistant to brute-force search attacks even with increasing computation power.
  • Secondly, we have overridden the configure() method to provide our implementation of the login method.
    • Whenever we use a custom form for authentication in place of the one provided by Spring Security, we have to inform Spring Security of it using the formLogin() method.
    • We then also specify our login URL – /login. We will map the URL to our custom login page in our Controller later.
    • We have also specified that the endpoints starting with /register, /login and the logout page need not be protected. We did so using the permitAll() method. This allows everyone to access these endpoints. Other than these endpoints, all endpoints are to be authenticated(). That is to say, users must be logged in to access all the other endpoints.
    • On logout, we have specified that the session is to be invalidated and authentication stored in the application’s SecurityContext be cleared.

Security Setup

Now, we will setup our authentication process. We are going to setup authentication using a database and locking of user accounts.

Let’s create our implementation of UserDetailsService first. As we have discussed before, we need to provide our custom implementation for authentication using a database. This is because, Spring Security, as we know, only provides an in-memory authentication implementation by default. Therefore, we need to override that implementation with our database based process. To do so, we need to override the loadUserByUsername() method of UserDetailsService.

UserDetailsService

package com.tutorial.spring.security.formlogin.security; 

import java.util.Optional; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.security.core.userdetails.UserDetails; 
import org.springframework.security.core.userdetails.UserDetailsService; 
import org.springframework.security.core.userdetails.UsernameNotFoundException; 
import org.springframework.security.provisioning.UserDetailsManager; 
import org.springframework.stereotype.Service; 
import com.tutorial.spring.security.formlogin.model.User; 
import com.tutorial.spring.security.formlogin.repository.UserRepository; 

@Service
public class SecurityUserDetailsService implements UserDetailsService { 
   @Autowired 
   private UserRepository userRepository; 
   
   @Override 
   public UserDetails loadUserByUsername(String username) 
   throws UsernameNotFoundException { 
      User user = userRepository.findUserByUsername(username) 
         .orElseThrow(() -< new UsernameNotFoundException("User not present")); 
         return user; 
   } 
   public void createUser(UserDetails user) { 
      userRepository.save((User) user); 
   } 
}

As we can see here, we have implemented the loadUserByUsername() method here. Here we are fetching the user from our database using the UserRepository interface. If the user is not found it throws UsernameNotFoundException.

We also have a createUser() method. We will use this method to add users to our database who have registered in our application using UserRepository.

Authentication Provider

We will now implement our custom authentication provider. It will implement the AuthenticationProvider interface. We have two methods here that we have to override and implement.

package com.tutorial.spring.security.formlogin.security; 

import java.util.Optional; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.security.authentication.AuthenticationProvider; 
import org.springframework.security.authentication.BadCredentialsException; 
import org.springframework.security.authentication.LockedException; 
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 
import org.springframework.security.core.Authentication; 
import org.springframework.security.core.AuthenticationException; 
import org.springframework.security.crypto.password.PasswordEncoder; 
import org.springframework.stereotype.Component;

import com.tutorial.spring.security.formlogin.model.Attempts; 
import com.tutorial.spring.security.formlogin.model.User; 
import com.tutorial.spring.security.formlogin.repository.AttemptsRepository;
import com.tutorial.spring.security.formlogin.repository.UserRepository;

@Component public class AuthProvider implements AuthenticationProvider {
   private static final int ATTEMPTS_LIMIT = 3; 
   
   @Autowired 
   private SecurityUserDetailsService userDetailsService; 
   @Autowired private PasswordEncoder passwordEncoder; 
   @Autowired private AttemptsRepository attemptsRepository; 
   @Autowired private UserRepository userRepository; 
   @Override 
   public Authentication authenticate(Authentication authentication) 
   throws AuthenticationException {
      String username = authentication.getName();

import com.tutorial.spring.security.formlogin.repository.UserRepository; 

@Component public class AuthProvider implements AuthenticationProvider { 
   private static final int ATTEMPTS_LIMIT = 3; 
   @Autowired private SecurityUserDetailsService userDetailsService; 
   @Autowired private PasswordEncoder passwordEncoder; 
   @Autowired private AttemptsRepository attemptsRepository; 
   @Autowired private UserRepository userRepository; 
   @Override 
   public Authentication authenticate(Authentication authentication) 
   throws AuthenticationException { 
      String username = authentication.getName();
      Optional<Attempts> 
      userAttempts = attemptsRepository.findAttemptsByUsername(username); 
      if (userAttempts.isPresent()) { 
         Attempts attempts = userAttempts.get();
         attempts.setAttempts(0); attemptsRepository.save(attempts); 
      } 
   } 
   private void processFailedAttempts(String username, User user) { 
      Optional<Attempts> 
      userAttempts = attemptsRepository.findAttemptsByUsername(username); 
      if (userAttempts.isEmpty()) { 
         Attempts attempts = new Attempts(); 
         attempts.setUsername(username); 
         attempts.setAttempts(1); 
         attemptsRepository.save(attempts); 
      } else {
         Attempts attempts = userAttempts.get(); 
         attempts.setAttempts(attempts.getAttempts() + 1); 
         attemptsRepository.save(attempts);
      
         if (attempts.getAttempts() + 1 > 
         ATTEMPTS_LIMIT) {
            user.setAccountNonLocked(false); 
            userRepository.save(user); 
            throw new LockedException("Too many invalid attempts. Account is locked!!"); 
         } 
      }
   }
   @Override public boolean supports(Class<?> authentication) { 
      return true; 
   }
}
  • authenticate() − This method returns a fully authenticated object including credentials on successful authentication. This object is then stored in the SecurityContext. To perform authentication we will use the loaduserByUsername() method of the SecurityUserDetailsService class of our Application. Here we perform multiple things −
    • First, we extract the user credentials from the Authentication request object which is passed as a parameter to our function. This authentication object was prepared by the AuthenticationFilter class and passed down the AuthenticationProvider through the AuthenticationManager.
    • We also fetch the user details from the database using the loadUserByUsername() method.
    • Now, first, we check if the user account has been locked due to previous failed authentication attempts. If we find that the account is locked, we throw a LockedException, and the user will be unable to authenticate unless the account is unlocked again.
    • If the account is not locked, we match the provided password along with the one stored against the user in the database. This is done using the matches() method of the PasswordEncoder interface.
    • If the passwords match, and the account has not been locked by then, we return a fully authenticated object. Here we have used an instance UsernamePasswordAuthenticationToken class (as it is a username-password authentication) that implements Authentication. Meanwhile, we also reset the attempts counter to 0.
    • On the other hand, if the password doesn’t match, we check for a few conditions −
      • If it is the user’s first attempt, then, probably his name would not be in the database. We check for this using the method findAttemptsByUsername() from the AttemptsRepository.
      • If not found, we make an entry for the user in the database, with the number of attempts set to one.
      • If a user is found, then we increase the number of attempts by 1.
      • We then check against the maximum number of failed attempts allowed, using a constant value we defined earlier.
      • If the number is more than the allowed number of attempts, then the user is locked our of the application and a LockedException is thrown.
  • supports() − We also have the supports method that checks if our authentication type is supported by our AuthenticationProvider implementation class. It returns true, false, or null if it matches, doesn’t match, or if it can’t decide respectively. We have hardcoded it to be true for now.

Controller

Now let’s create our controller package. It will contain our HelloController class. Using this controller class we will map our views to the endpoints and serve those views when the respective endpoints are hit. We will also autowire the PasswordEncoder and the UserDetailsService classes in this component. These injected dependencies will be used in creating our user. Let’s now create our endpoints.

package com.tutorial.spring.security.formlogin.controller; 

import java.util.Map;
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpSession; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.http.MediaType; 
import org.springframework.security.authentication.BadCredentialsException; 
import org.springframework.security.authentication.LockedException; 
import org.springframework.security.crypto.password.PasswordEncoder; 
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.GetMapping; 
import org.springframework.web.bind.annotation.PostMapping; 
import org.springframework.web.bind.annotation.RequestParam; 
import com.tutorial.spring.security.formlogin.model.User; 
import com.tutorial.spring.security.formlogin.security.SecurityUserDetailsService; 
@Controller 
public class HelloController {         
   @Autowired private SecurityUserDetailsService userDetailsManager; 
   @Autowired
   private PasswordEncoder passwordEncoder; 
   
   @GetMapping("/") 
   public String index() { 
      return "index"; 
   }
   @GetMapping("/login") 
   public String login(HttpServletRequest request, HttpSession session) { 
      session.setAttribute(
         "error", getErrorMessage(request, "SPRING_SECURITY_LAST_EXCEPTION")
      ); 
      return "login"; 
   } 
   @GetMapping("/register") 
   public String register() {  
      return "register"; 
   } 
   @PostMapping(
      value = "/register", 
      consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, produces = { 
      MediaType.APPLICATION_ATOM_XML_VALUE, MediaType.APPLICATION_JSON_VALUE }
   )
   public void addUser(@RequestParam Map<String, String> body) {
      User user = new User(); user.setUsername(body.get("username")); 
      user.setPassword(passwordEncoder.encode(body.get("password"))); 
      user.setAccountNonLocked(true); userDetailsManager.createUser(user); 
   }
   private String getErrorMessage(HttpServletRequest request, String key) {
      Exception exception = (Exception) request.getSession().getAttribute(key); 
      String error = ""; 
      if (exception instanceof BadCredentialsException) { 
         error = "Invalid username and password!"; 
      } else if (exception instanceof LockedException) { 
         error = exception.getMessage(); 
      } else { 
         error = "Invalid username and password!"; 
      } 
      return error;
   }
}
  • index ("/") – This endpoint will serve the index page of our application. As we have configured earlier, we shall be protecting this page and allow only authenticated users will be able to access this page.
  • login ("/login") – This will be used to serve our custom login page, as mentioned earlier. Any unauthenticated user will be redirected to this endpoint for authentication.
  • register("/register") (GET) – We will have two “register” endpoints for our application. One will be to serve the registration page. The other one will be to handle the registration process. So, the former one will use an Http GET and the latter will be a POST endpoint.
  • register("/register") (POST) – We will use this endpoint to handle the user registration process. We will get the user name and password from the parameters. Then we will encode the password using the passwordEncoder that we have @Autowired into this component. We also set user account as unlocked at this point. We will then save this user data in our users table with the createUser() method.

In addition to the above, we have the getErrorMessage() method. It is used to determine the last thrown exception to add a message in our login template. This way, we can be aware of authentication errors and display proper messages.

Resources

We have created our endpoints, the only thing left is to create our views.

First, we will create our index page. This page will be accessible to users only on successful authentication. This page has access to the Servlet request object using which we can display the user name of the logged in user.

<!DOCTYPE html> 
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3"> 
   <head> 
      <title>
         Hello World!
      </title> 
   </head>
   <body> 
      <h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1> 
      <form th:action="@{/logout}" method="post"> 
         <input type="submit" value="Sign Out"/> 
         </form>
   </body> 
<html> 

Next, we create our login view. This display our custom login form with the username and password fields. This view will also be rendered in case of a logout or failed authentication and will display appropriate messages for each case.

<!DOCTYPE html> 
<html xmlns="http://www.w3.org/1999/xhtml"      xmlns:th="https://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3"> 
   <head> 
      <title>Spring Security Example</title> 
   </head> 
   <body> 
   <div th:if="${param.error}"> 
      <p th:text="${session.error}" th:unless="${session == null}">[...]</p> 
   </div> 
   <div th:if="${param.logout}">You have been logged out.</div> 
   <form th:action="@{/login}" method="post>
   <div> 
      <label> User Name : <input type="text" name="username" /> </label> 
   </div> 
   <div> 
   <label> Password: <input type="password" name="password" /> </label> 
   </div> 
   <div> 
      <input type="submit" value="Sign In" /> </div> 
      </form> 
   </body>
</html>

Moving, we create our required view, the register view. This view will let users register themselves with the application. This user data will be stored in the database which will then be used for authentication.

<!DOCTYPE html> 
<html> 
   <head> 
      <meta charset="ISO-8859-1"> 
      <title>Insert title here</title>
   </head> 
   <body> 
      <form action="/register" method="post"> 
         <div class="container"> 
            <h1>Register</h1> 
            <p>Please fill in this form to create an account.</p> 
            <hr> 

            <label for="username">
            <b>Username</b>
            </label> 
            <input type="text" placeholder="Enter Username" name="username" id="username" required> 

            <label for="password"><b>Password</b></label> 
            <input type="password" placeholder="Enter Password" name="password" id="password" required> 

            <button type="submit" class="registerbtn">Register</button> 
         </div> 

      </form> 
   </body> 
</html>

Final Project Structure

Our final project structure should look something similar to this.

Form Login

Running the Application

We can then run the application as a SpringBootApp. When we go localhost:8080 on our browser it will redirect us back to the login page.

Running the Application

On successful authentication it will take us the index view with a greeting.

Hello Users

Since, we have allowed only three failed attempts before the account gets locked, so on the third failed authentication the user get locked and the message is displayed on the screen.

Third Failed Authentication

On hitting the /register endpoint we can also register a new user.

Register

Conclusion

From today’s article, we have learned how to use a custom form for login using a database using an annotation-based configuration. We have also learned how to prevent multiple failed login attempts. While doing we have seen how we can implement our own AuthenticationProvider and UserDetailsService to authenticate users using our custom authentication process.

Spring Security - Form Login, Remember Me and Logout

Contents

  • Introduction and Overview
  • Getting Started (Practical Guide)

Introduction and Overview

Spring Security comes with a ton of built-in features and tools for our convenience. In this example, we are going to discuss three of those interesting and useful features −

  • Form-login
  • Remember Me
  • Logout

Form Login

Form-based login is one form of Username/password authentication that Spring Security provides support for. This is provided through an Html form.

Whenever a user requests a protected resource, Spring Security checks for the authentication of the request. If the request is not authenticated/authorized, the user will be redirected to the login page. The login page must be somehow rendered by the application. Spring Security provides that login form by default.

Moreover, any other configuration, if needed, must be explicitly provided as given below −

protected void configure(HttpSecurity http) throws Exception {
http 
   // ... 
   .formLogin(
      form -> form       .loginPage("/login") 
      .permitAll() 
   ); 
}

This code requires a login.html file to be present in the templates folder which would be returned on hitting the /login. This HTML file should contain a login form. Furthermore, the request should be a post request to /login. The parameter names should be “username” and “password” for username and password respectively. In addition to this, a CSRF Token also needs to be included with the form.

The above code snippet will be clearer once we are done with code exercise.

Remember Me

This type of authentication requires a remember-me cookie to be sent to the browser. This cookie stores user information/authentication principal and it is stored in the browser. So, the website can remember the identity of the user next time when the session is started. Spring Security has the necessary implementations in place for this operation. One uses hashing to preserve the security of cookie-based tokens while the other uses a database or other persistent storage mechanism to store the generated tokens.

Logout

The default URL /logout logs the user out by−

  • Invalidating the HTTP Session
  • Cleaning up any RememberMe authentication that was configured
  • Clearing the SecurityContextHolder
  • Redirect to /login?logout

WebSecurityConfigurerAdapter automatically applies logout capabilities to the Spring Boot application.

Getting Started (Practical Guide) As usual, we shall start by going to start.spring.io. Here we choose a maven project. We name the project “formlogin” and choose the desired Java version. I am choosing Java 8 for this example. We also go on to add the following dependencies −

  • Spring Web
  • Spring Security
  • Thymeleaf
  • Spring Boot DevTools
Spring Initializr

Thymeleaf is a templating engine for Java. It allows us to quickly develop static or dynamic web pages for rendering in the browser. It is extremely extensible and allows us to define and customize the processing of our templates in fine detail. In addition to this, we can learn more about Thymeleaf by clicking this link.

Let’s move on to generate our project and download it. We then extract it to a folder of our choice and use any IDE to open it. I shall be using Spring Tools Suite 4. It is available for free downloading from the https://spring.io/tools website and is optimized for spring applications.

Let’s take a look at our pom.xml file. It should look something similar to this −

<?xml version="1.0" encoding="UTF-8"?> 
<project xmlns="http://maven.apache.org/POM/4.0.0"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion> 
   <parent> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-parent</artifactId> 
      <version>2.3.1.RELEASE</version> 
      <relativePath /> 
      <!-- lookup parent from repository --> 
   </parent> 
   <groupId>            com.spring.security</groupId> 
   <artifactId>formlogin</artifactId> 
   <version>0.0.1-SNAPSHOT</version> 
   <name>formlogin</name> 
   <description>Demo project for Spring Boot</description> 
      
   <properties> 
      <java.version>1.8</java.version> 
   </properties> 
      
   <dependencies> 
      <dependency> 
         <groupId>org.springframework.boot</groupId> 
         <artifactId>spring-boot-starter-security</artifactId>
      </dependency> 
   <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-web</artifactId> 
   </dependency> 
   <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-thymeleaf</artifactId> 
   </dependency> 
   <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-devtools</artifactId> 
   </dependency> 
   <dependency> 
   <groupId>org.springframework.boot</groupId> 
   <artifactId>spring-boot-starter-test</artifactId> 
   <scope>test</scope> 
   <exclusions> 
      <exclusion> 
         <groupId>org.junit.vintage</groupId>
         <artifactId>junit-vintage-engine</artifactId> 
      </exclusion> 
   </exclusions> 
   </dependency> 
   <dependency> 
      <groupId>org.springframework.security</groupId> 
      <artifactId>spring-security-test</artifactId> 
      <scope>test</scope> 
   </dependency> 
   </dependencies> 

   <build> 
      <plugins> 
         <plugin> 
            <groupId>org.springframework.boot</groupId> 
            <artifactId>spring-boot-maven-plugin</artifactId> 
         </plugin> 
      </plugins> 
   </build>
</project>

Let’s create a package in our folder /src/main/java under the default package. We shall be naming it as config as we would place all our configuration classes here. So, the name should look something similar to this – com.tutorial.spring.security.formlogin.config.

The Configuration Class

package com.tutorial.spring.security.formlogin.config; 

import java.util.List; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.security.config.annotation.web.builders.HttpSecurity; 
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 
import org.springframework.security.core.userdetails.User; 
import org.springframework.security.core.userdetails.UserDetails; 
import org.springframework.security.core.userdetails.UserDetailsService; 
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 
import org.springframework.security.crypto.password.NoOpPasswordEncoder; 
import org.springframework.security.crypto.password.PasswordEncoder; 
import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; 

import com.spring.security.formlogin.AuthFilter;
 
@Configuration 
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 
   
   @Bean 
   protected UserDetailsService userDetailsService() {
   UserDetailsManager userDetailsManager = new InMemoryUserDetailsManager(); 
   UserDetails user = User.withUsername("abby") 
   .password(passwordEncoder().encode("12345")) 
      .authorities("read") .build(); 
      userDetailsManager.createUser(user); 
      return userDetailsManager; 
      
   }
   @Bean 
   public PasswordEncoder passwordEncoder() { 
      return new BCryptPasswordEncoder(); }; 
      @Override 
      protected void configure(HttpSecurity http) throws Exception { 
      http.csrf().disable() .authorizeRequests().anyRequest()
      .authenticated() .and() 
      .formLogin() 
      .and() 
      .rememberMe() 
      .and() .logout() .logoutUrl("/logout") 
      .logoutSuccessUrl("/login") .deleteCookies("remember-me"); 
   } 
}

Code Breakdown

Inside of our config package, we have created the WebSecurityConfig class. This class extends the WebSecurityConfigurerAdapter of Spring Security. We shall be using this class for our security configurations, so let’s annotate it with an @Configuration annotation. As a result, Spring Security knows to treat this class a configuration class. As we can see, configuring applications have been made very easy by Spring.

Let’s take a look at our configuration class.

  • First, we shall create a bean of our UserDetailsService class by using the userDetailsService() method. We shall be using this bean for managing our users for this application. Here, to keep things simple, we shall use an InMemoryUserDetailsManager instance to create a user. This user, along with our given username and password, will contain a simple “read” authority.
  • Now, let’s look at our PasswordEncoder. We shall be using a BCryptPasswordEncoder instance for this example. Hence, while creating the user, we used the passwordEncoder to encode our plaintext password like this
.password(passwordEncoder().encode("12345"))
  • After the above steps, we move on to our next configuration. Here, we override the configure method of WebSecurityConfigurerAdapter class. This method takes HttpSecurity as a parameter. We shall be configuring this to use our form login and logout, as well as a remember-me function.

Http Security Configuration

We can observe that all these functionalities are available in Spring Security. Let’s study the below section in detail −

http.csrf().disable()         
   .authorizeRequests().anyRequest().authenticated() 
   .and() 
   .formLogin() 
   .and() 
   .rememberMe() 
   .and() 
   .logout()
   .logoutUrl("/logout") .logoutSuccessUrl("/login") .deleteCookies("remember-me");

There are a few points to note here −

  • We have disabled csrf or Cross-Site Request Forgery protection As this is a simple application only for demonstration purposes, we can safely disable this for now.
  • Then we add configuration which requires all requests to be authenticated. As we shall see later, we will have a single “/” endpoint for the index page of this application, for simplicity.
  • After that, we shall be using the formLogin() functionality of Spring Security as mentioned above. This generates a simple login page.
  • Then, we use the rememberMe() functionality of Spring Security. This will perform two things.
    • Firstly, it will add a “Remember Me” checkbox to our default login form that we generated using formLogin().
    • And, secondly, ticking the checkbox generates the remember-me cookie. The cookie stores the identity of the user and the browser stores it. Spring Security detects the cookie in future sessions to automate the login.

    As a result, the user can access the application again without logging in again.

  • And lastly, we have the logout() functionality. For this too, a default functionality has been provided by Spring security. Here it performs two important functions −
    • Invalidates the Http session, and unbinds objects bound to the session.
    • It clears the remember-me cookie.
    • Removes the authentication from Spring’s Security context.

    We also, provided a logoutSuccessUrl(), so that the application comes back to the login page after logout. This completes our application configuration.

The Protected Content (Optional)

We shall now create a dummy index page now for the user to view when he logs in. It will also contain a logout button.

In our /src/main/resources/templates, we add a index.html file.Then add some Html content to it.

<!doctype html> 
<html lang="en"> 
   <head> 
      <!-- Required meta tags -->
      <meta charset="utf-8"> 
      <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> 
      <!-- Bootstrap CSS --> 
      <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous"> 
      <title>Hello, world!</title> 
   </head> 
   <body> 
      <h1>Hello, world!</h1> <a href="logout">logout</a> 
      <!-- Optional JavaScript --> 
      <!-- jQuery first, then Popper.js, then Bootstrap JS --> 
      <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> 
      <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script> 
      <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"
      integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script> 
   </body> 
</html>

This content is from Bootstrap 4 getting started template.

We also add

<a href="logout">logout</a>

to our file, so as the user can log out of the application using this link.

The Resource Controller

We have created the protected resource, we now add the controller to serve this resource.

package com.tutorial.spring.security.formlogin.controllers; 
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.GetMapping; 
@Controller public class AuthController { 
   @GetMapping("/") public String home() { return "index"; }
}

As we can see, it is a very simple controller. It only has a get endpoint which serves our index.html file when the start our application.

Running the application

Let’s run the application as a Spring Boot Application. We can go to http://localhost:8080 on our browser when the application starts. It should ask us for username and password. Additionally, we shall also be able to see the remember-me checkbox.

Sign In

Login Page

Now, if we provide the user information as we had configured in our WebSecurity config file, we shall be able to log in. Also, if we tick the remember-me checkbox, we shall be able to see the remember-me cookie in our browser’s developer tools section.

Console Application Console Network

As we can see the cookie is sent along with our login request.

Also, included in the web page is a link for log out. On clicking the link, we shall be logged out of our application and sent back to our login page.

Spring Security - Taglib

Contents

  • Introduction and Overview
  • Spring Security Tags
    • The authorize Tag
    • The authentication tag
    • The csrfInput Tag
    • The csrfMetaTags Tag
  • Getting Started (Practical Guide)

Introduction and Overview

In Spring MVC applications using JSP, we can use the Spring Security tags for applying security constraints as well as for accessing security information. Spring Security Tag library provides basic support for such operations. Using such tags, we can control the information displayed to the user based on his roles or permissions. Also, we can include CSRF protection features in our forms.

To use Spring security tags, we must have the security taglib declared in our JSP file.

<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>

Now, we can use Spring Security tags with the “sec” prefix. Let’s now see the usage of the tags.

The authorize Tag

The first tag we will be discussing is the authorize tag. Let’s check out some usage examples.

<sec:authorize access="!isAuthenticated()"> Login </sec:authorize> 
<sec:authorize access="isAuthenticated()"> Logout </sec:authorize> 
<sec:authorize access="hasRole('ADMIN')"> Hello Admin. </sec:authorize>

As we can see, we can use this tag to hide or show sections of information based on access or roles. To evaluate roles or access we also use the following Spring Security Expressions

  • hasRole(“ADMIN”) − evaluates to true if the current user has the admin role.

  • hasAnyRole(‘ADMIN’,’USER’) − evaluates to true if the current user has any of the listed roles

  • isAnonymous() − evaluates to true if the current user is an anonymous user

  • isRememberMe() − evaluates to true if the current user is a remember-me user

  • isFullyAuthenticated() − evaluates to true if the user is authenticated and is neither anonymous nor a remember-me user

As we can see, the access attribute is where the web-security expression is specified. Then, Spring Security evaluates the expression The evaluation is generally delegated to SecurityExpressionHandler<FilterInvocation>, which is defined in the application context. If it returns true, then the user can get access to the information given in that section.

If we use the authorize tag with Spring Security ‘s Permission Evaluator, we can also check user permissions as given below −

<sec:authorize access="hasPermission(#domain,'read') or hasPermission(#domain,'write')">
   This content is visible to users who have read or write permission.
</sec:authorize>

We can also allow or restrict the user from clicking on certain links within our content.

<sec:authorize url="/admin">
   This content will only be visible to users who are authorized to send requests to the "/admin" URL. 
</sec:authorize>

The authentication tag

When we want access to the current Authentication object stored in the Spring Security Context, we can use the authentication tag. Then we can use it to render properties of the object directly in our JSP page. For example, if we want to render the principal property of the Authentication object in our page, we can do it as follows −

<sec:authentication property="principal.username" />

The csrfInput Tag

We can use the csrfInput tag to insert a hidden form field with the correct values for the CSRF protection token when CSRF protection is enabled. If CSRF protection is not enabled, this tag outputs nothing.

We can place the tag within the HTML <form></form> block along with other input fields. However, we must not place the tag within the <form:form></form:form> block as Spring Security automatically inserts a CSRF form field within those tags and also takes care of Spring forms automatically.

<form method="post" action="/do/something"> 
   <sec:csrfInput /> 
   Username:<br /> 
   <input type="text" username="username" /> 
   ... 
</form>

The csrfMetaTags Tag

We can use this tag to insert meta tags which contain the CSRF protection token form field and header names and CSRF protection token value. These meta tags can be useful for employing CSRF protection within Javascript in our application. However, this tag only works when we have enabled CSRF protection in our application, otherwise, this tag outputs nothing.

<html> 
   <head> 
      <title>CSRF Protection in Javascript</title> 
      <sec:csrfMetaTags /> 
      <script type="text/javascript" language="javascript"> 
         var csrfParam = $("meta[name='_csrf_param']").attr("content"); 
         var csrfToken = $("meta[name='_csrf']").attr("content");
      </script> 
   </head> 
   <body> 
      ... 
   </body> 
</html>

Getting Started (Practical Guide)

Now that we have discussed the tags, let’s build an application to demonstrate the usage of the tags. We shall be using Spring Tool Suite 4 as our IDE. Additionally, we shall be using the Apache Tomcat server to serve our application. So, let’s get started.

Setting up the Application

Let’s create a simple Maven Project in STS. We can name our application as taglibsdemo, and package it as a .war file.

New Maven Project New Maven Project War

When we have finished setting up our application it should have a structure similar to this.

Finished Setup

The pom.xml file

We shall add these following dependencies to our application −

  • Spring Web MVC
  • Spring-Security-Web
  • Spring-Security-Core
  • Spring-Security-Taglibs
  • Spring-Security-Config
  • Javax Servlet Api
  • JSTL

After adding these dependencies, our pom.xml should look similar to this −

<project xmlns="http://maven.apache.org/POM/4.0.0" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
   https://maven.apache.org/xsd/maven-4.0.0.xsd"> 
   <modelVersion>4.0.0</modelVersion> 
   <groupId>com.tutorial.spring.security</groupId> 
   <artifactId>taglibsdemo</artifactId> 
   <version>0.0.1-SNAPSHOT</version> 
   <packaging>war</packaging> 
   <properties> 
      <maven.compiler.target>1.8</maven.compiler.target> 
      <maven.compiler.source>1.8</maven.compiler.source> 
   </properties> 
   <dependencies> 
      <dependency> 
         <groupId>org.springframework</groupId> 
         <artifactId>spring-webmvc</artifactId>
         <version>5.0.2.RELEASE</version> 
      </dependency>
      <dependency> 
         <groupId>org.springframework.security</groupId> 
         <artifactId>spring-security-web</artifactId> 
         <version>5.0.0.RELEASE</version>
      </dependency> 
      <dependency> 
         <groupId>org.springframework.security</groupId> 
         <artifactId>spring-security-core</artifactId> 
         <version>5.0.4.RELEASE</version> 
      </dependency> 
      <dependency> 
         <groupId>org.springframework.security</groupId> 
         <artifactId>spring-security-taglibs</artifactId> 
         <version>5.0.4.RELEASE</version> 
      </dependency> 
      <dependency> 
         <groupId>org.springframework.security</groupId> 
         <artifactId>spring-security-config</artifactId> 
         <version>5.0.4.RELEASE</version> 
      </dependency>
      <dependency> 
         <groupId>javax.servlet</groupId> 
         <artifactId>javax.servlet-api</artifactId> 
         <version>3.1.0</version> 
         <scope>provided</scope> 
         </dependency> <dependency> 
         <groupId>javax.servlet</groupId> 
         <artifactId>jstl</artifactId> 
         <version>1.2</version> 
      </dependency> 
   </dependencies> 
   <build> 
      <plugins> 
         <plugin> 
            <groupId>org.apache.maven.plugins</groupId> 
            <artifactId>maven-war-plugin</artifactId> 
            <version>2.6</version> 
            <configuration> 
            <failOnMissingWebXml>false</failOnMissingWebXml> 
            </configuration>
         </plugin> 
      </plugins> 
   </build> 
</project>

Let’s create our base package for the application. We can name it com.taglibsdemo. Within the package, let’s create another package for our configuration files. Since, it will be holding the configuration files, we can name it config.

ApplicationConfig.java

Let’s create our first configuration class ApplicationConfig.java.

package com.taglibsdemo.config; 

import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.ComponentScan; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.web.servlet.config.annotation.EnableWebMvc; 
import org.springframework.web.servlet.view.InternalResourceViewResolver; 
import org.springframework.web.servlet.view.JstlView; 

@EnableWebMvc 
@Configuration @ComponentScan({ "com.taglibsdemo.controller"} ) 
public class ApplicationConfig { 
   @Bean
   public InternalResourceViewResolver viewResolver() {   
      InternalResourceViewResolver 
      viewResolver = new InternalResourceViewResolver(); 
      viewResolver.setViewClass(JstlView.class); 
      viewResolver.setPrefix("/WEB-INF/views/"); 
      viewResolver.setSuffix(".jsp"); return viewResolver; 
   }
}

Let’s break down the code here −

  • @EnableWebMvc − We use @EnableWebMvc to enable Spring MVC. So, we add this annotation to an @Configuration class to import the Spring MVC configuration from WebMvcConfigurationSupport. WebMvcConfigurationSupport is the main class that provides the configuration for the MVC Java config. Not using this annotation may result in things like content-type and accept header, generally content negotiation not working. @EnableWebMvc registers a RequestMappingHandlerMapping, a RequestMappingHandlerAdapter, and an ExceptionHandlerExceptionResolver among others in support of processing requests with annotated controller methods using annotations such as @RequestMapping , @ExceptionHandler, and others.
    • @Configuration − This annotation indicates that the class declares one or more @Bean methods and may be processed by the Spring IoC container to generate bean definitions and service requests for those beans at runtime. A @Configuration class is typically bootstrapped using either AnnotationConfigApplicationContext or its web-capable variant, AnnotationConfigWebApplicationContext.
  • @ComponentScan − @ComponentScan annotation is used to tell Spring the packages to scan for annotated components. @ComponentScan also used to specify base packages and base package classes using thebasePackageClasses or basePackages attributes of @ComponentScan.
  • InternalResourceViewResolver − To resolve the provided URI to the actual URI in the format prefix + viewname + suffix.
  • setViewClass() − To set the view class that should be used to create views.
  • setPrefix() − To set the prefix that gets prepended to view names when building a URL.
  • setSuffix() − To set the suffix that gets appended to view names when building a URL.

WebSecurityConfig.java

Next we shall create our WebSecurityConfig class which will extend the familiar WebSecurityConfigurerAdapter class of Spring Security.

package com.taglibsdemo.config; 

import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.ComponentScan; 
import org.springframework.security.config.annotation.web.builders.HttpSecurity; 
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 
import org.springframework.security.core.userdetails.UserDetailsService; 
import org.springframework.security.provisioning.InMemoryUserDetailsManager; 
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; 
import org.springframework.security.core.userdetails.User; 
import org.springframework.security.core.userdetails.User.UserBuilder;
@EnableWebSecurity @ComponentScan("com.taglibsdemo")  
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 
   @SuppressWarnings("deprecation") @Bean 
   public UserDetailsService userdetailsService() { 
      UserBuilder users = User.withDefaultPasswordEncoder(); 
      InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
      manager.createUser(users.username("rony").password("rony123").roles("USER").build()); 
      manager.createUser(users.username("admin").password("admin123").roles("ADMIN").build()); 
      return manager; 
   } 
   @Override protected void configure(HttpSecurity http) throws Exception { 
      http.authorizeRequests() .antMatchers("/index", "/").permitAll()
      .antMatchers("/admin", "/user").authenticated() .and() .formLogin() 
      .and() .logout() .logoutRequestMatcher(
         new AntPathRequestMatcher("/logout")
      ); 
   }
}

Let’s break the code down here −

  • WebSecurityConfigurerAdapter − The abstract class that implements WebSecurityConfigurer WebSecurityConfigurer and allows us to override methods for security configuration.
  • @EnableWebSecurity − It enables Spring to automatically find and apply the @Configuration class to the global WebSecurity.
  • We then create a UserDetailsService Bean using the method to create users using the InMemoryUserDetailsManager instance. We create two users – one with role “USER” and another with role “ADMIN” and add them to Spring Security.
  • After that, we override the configure method with HttpSecurity as a parameter. We make our home page or index page accessible to all and admin page to be accessible when the user is authenticated. Next, we add Spring Security form login and logout.

So, with those steps our security configuration is complete. Now, we are ready to move on to the next step.

SpringSecurityApplicationInitializer.java

Moving on, now we shall create the SpringSecurityApplicationInitializer.java class which extends the AbstractSecurityWebApplicationInitializer class of Spring Security.

package com.taglibsdemo.config;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SpringSecurityApplicationInitializer extends 
AbstractSecurityWebApplicationInitializer { }

AbstractSecurityWebApplicationInitializer is an abstract class that implements Spring’s WebApplicationInitializer. So, SpringServletContainerInitializer will initialize the concrete implementations of this class if the classpath contains spring-web module.

MvcWebApplicationInitializer.java

package com.taglibsdemo.config; 
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; 
public class MvcWebApplicationInitializer extends 
AbstractAnnotationConfigDispatcherServletInitializer { 
   @Override protected Class</?>[] getRootConfigClasses() { 
      return new Class[] {WebSecurityConfig.class}; 
   }
   @Override protected Class</?>[] getServletConfigClasses() { 
      return null; 
   } 
   @Override protected String[] getServletMappings() { 
      return new String[] {"/"}; 
   }
}
  • AbstractAnnotationConfigDispatcherServletInitializer − This class extends WebApplicationInitializer. We need this class as a base class for initializing a Spring application in Servlet container environment.As a result, the subclass of AbstractAnnotationConfigDispatcherServletInitializer will provide the classes annotated with @Configuration, Servlet config classes and DispatcherServlet mapping pattern.
  • getRootConfigClasses() − This method must be implemented by the class extending AbstractAnnotationConfigDispatcherServletInitializer. It provides “root” application context configuration.
  • getServletConfigClasses() − This method too, must be implemented to provide DispatcherServlet application context configuration.
  • getServletMappings() − This method is used specify the servlet mapping(s) for the DispatcherServlet.

We have set up the configuration classes. Now , we shall create our controller to serve the JSP pages.

HelloController.java

package com.taglibsdemo.controller;
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.GetMapping; 
@Controller public class HelloController { 
@GetMapping("/") 
   public String index() { return "index"; } 
   @GetMapping("/user") 
   public String user() { return "admin"; } 
   @GetMapping("/admin") 
   public String admin() { return "admin"; } 
}

Here, we have created three endpoints – “/”, “/user”, and “/admin”. As specified in our configuration previously, we will allow unauthorized access to the index page

“/”. On the other hand, the “/user” and “/admin” endpoints would be authorized only access.

Secure Content to serve

Moving on, we shall now create the JSP pages which are to be served on hitting the specific endpoints.

For this, inside our src/main folder we create a folder called webapp. Inside this folder, we create our WEB-INF folder and further as in ApplicationConfig.java class we add the views folder. Here, in this folder we shall be adding the views.

Let’s add our home page, i.e., index.jsp first.

<%@ page language="java" contentType="text/html;
   charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> 
<!DOCTYPE html> 
<html> 
   <head> 
      <meta charset="ISO-8859-1"> 
      <title>Home Page</title> 
   </head>
   <body> 
      <a href="user">User</a> 
      <a href="admin">Admin</a> 
      <br> 
      <br> Welcome to the Application! 
   </body> 
</html>

Then we shall create our admin.jsp file. Let’s add it.

<%@ page language="java" contentType="text/html; 
   charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> 
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="security"%> 
<!DOCTYPE html> 
<html> 
   <head> 
      <meta charset="ISO-8859-1"> 
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
      <title>Insert title here</title> 
   </head> 
   <body> Welcome to Admin Page! <a href="logout"> Logout </a> 
      <br> 
      <br> 
      <security:authorize access="hasRole('ADMIN')"> Hello Admin! 
      </security:authorize> 
   </body> 
</html>

here, we have added <%@ taglib uri="http://www.springframework.org/security/tags" prefix="security"%>. This is going to let us the Spring security tag libs as discussed before. As we can see, we have the added the “authorize” tag around the content. This content is will be only accessible by our admin. Any other user accessing this page will not be able to view this content.

Running the application

We now right click on the project and choose Run On Server. When the server starts and our application is running we can go to localhost:8080/taglibsdemo/ on our browser to view the page.

Running Application Login Page

Login page

Now, if we click on the User link in our application, we shall be asked to log in.

Here, as we can see in our controller, we are serving the admin page for bothe the user and admin links. But our user, if he is not an admin cannot view the content which is protected by our “authorize”tag.

Let’s log in as the user first.

Welcome to Admin Page

We can see that the “Hello Admin!” content is not visible to us. This is because the current user doesn’t have the admin role.

Let’s logout and log in as admin now.

Admin Page

We are now able to see the protected content “Hello Admin!” as the current user has the admin role.

Conclusion

We have learnt how we can use the Spring Security tag library to protect our content and get access to the current Authentication object in Our Spring Security Context.

Spring Security - XML Configuration

Contents

  • Fundamentals
  • Getting started (Practical Guide)

Fundamentals

In this we are going to discuss how to configure Spring Security with XML configuration. We shall be developing a simple Spring application with Spring Security. While doing so, we will discuss in details about each component that we are using.

Authentication and Authorization

  • Authentication − Authentication is ensuring the user or the client is who they claim to be. There are many ways in which Spring Security enables us to perform authentication. Spring Security supports Basic Authentication, LDAP authentication, JDBC authentication, etc.
  • Authorization − Ensuring if the user has permission for the action. If our application is a complex one, with different kinds of users such as admins, regular users, other less privileged users, we need to maintain access control in our application. For example, a guest user should not be able to access admin content. So, to control access to various resources within our application, we need to check if a user has permission to access that resource.

The above topics are the two main components of Spring Security. Spring security provided us with various in-built features to implement authentication and authorization in our application. We can use these features with our changes to secure an application very quickly. In addition to this, Spring Security also allows plenty of customizations to the features mentioned before to implement our own complex authentications and authorizations.

Getting Started (Practical Guide)

Let’s look at a basic example using in-built Spring Security features. In this example, we shall be securing our application with options provided out-of-box by Spring security. This is will give us an idea of the various components of Spring Security and how we can use them for our application. We shall be using XML to configure our application’s Security features.

The tools we shall be using for our application will be Spring Tool Suite 4 and Apache Tomcat Server 9.0. They are both available for free download and use.

First, let’s start a new simple Maven Project in STS. We can choose group id, artifact id as per our choice. After that, we click on Finish. As a result, we have added our project to our workspace. Let’s give STS some time to build and validate our project.

Simple Maven Project Project Structure

Our project structure would finally look similar to this.

XML Configuration Demo

Next, let’s add the dependencies. We are going to choose the following dependencies.

  • Spring Web MVC
  • Spring-Security-Web
  • Spring-Security-Core
  • Spring-Security-Config
  • Javax Servlet API

pom.xml

With these dependencies added, we are ready to configure our project. Let’s take a look at our pom.xml file.

<project xmlns="http://maven.apache.org/POM/4.0.0" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
   https://maven.apache.org/xsd/maven-4.0.0.xsd> 
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.tutorial.spring.security</groupId> 
      <artifactId>xmlconfigurationdemo</artifactId> 
      <version>0.0.1-SNAPSHOT</version> 
      <packaging>war</packaging> 
      <name>Spring Security with XML configuration</name> <description>Spring Security with XML configuration</description> 
      <properties> 
      <maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.source>1.8</maven.compiler.source> </properties> 
      <dependencies> 
      <dependency> 
         <groupId>org.springframework</groupId> 
         <artifactId>spring-webmvc</artifactId> 
         <version>5.0.2.RELEASE<version> 
         </dependency> <dependency> 
         <groupId>org.springframework.security</groupId> 
         <artifactId>spring-security-web</artifactId> 
         <version>5.0.0.RELEASE</version> 
      </dependency>
      <dependency> 
         <groupId>org.springframework.security</groupId> 
         <artifactId>spring-security-core</artifactId> 
         <version>5.0.0.RELEASE</version> 
      </dependency> 
      <dependency> 
         <groupId>org.springframework.security</groupId> 
         <artifactId>spring-security-config</artifactId> 
         <version>5.0.0.RELEASE</version> 
      </dependency> 
      <dependency> 
         <groupId>javax.servlet</groupId> 
         <artifactId>javax.servlet-api</artifactId> 
         <version>3.1.0</version> 
         <scope>provided</scope> 
      </dependency> 
      </dependencies> 
      <build> 
         <plugins> 
            <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>2.6</version> 
            <configuration>
               <failOnMissingWebXml>false</failOnMissingWebXml> 
            </configuration> 
         </plugin> 
      </plugins> 
   </build> 
</project>

Controller and views

First, We are going to create our controller. So, let’s create a package called controller and add our HomeController class to the package.

package com.tutorial.spring.security.xmlconfigurationdemo.controller; 
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.RequestMethod; 
@Controller public class HomeController { @GetMapping("/")
public String index() { return "index"; } 
@GetMapping("/admin") 
public String admin() { return "admin"; } }

Here, we have two endpoints – “index” and “admin”. While the index page is will be accessible to all, we will protect our “admin” page.

Since, we have created the routes, let’s also add the pages.

In our /src/main/webapp folder, let’s create a folder called WEB-INF. Then inside it, we will create a folder called views where we will create our views.

Let’s create our first view−

<%@ page language="java" contentType="text/html; 
charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> 
<!DOCTYPE html> 
<html> 
   <head> 
      <meta charset="ISO-8859-1"> <title>Insert title here</title> 
   </head> 
   <body> 
      <h2>Welcome to Spring Security!</h2>
   </body> 
</html>

Then we create our admin view.

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> 
<DOCTYPE html> 
<html> 
   <head> 
      <meta charset="ISO-8859-1"> <title>Insert title here</title> 
   </head> 
   <body> 
      Hello Admin 
   </body> 
</html>

Moving on, let’s configure our application.

Configurations.

web.xml

Now, let’s add our first xml file – the web.xml file.

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE xml> 
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
   http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <servlet> 
   <servlet-name>spring</servlet-name> 
   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 
   <init-param> 
      <param-name>contextConfigLocation</param-name> 
      <param-value>/WEB-INF/app-config.xml</param-value> 
   </init-param> 
   <load-on-startup>1</load-on-startup> 
   </servlet> 
   <servlet-mapping> 
   <servlet-name>spring</servlet-name> 
   <url-pattern>/</url-pattern> 
   </servlet-mapping> 
   <listener> 
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
   </listener> 
   <context-param> 
      <param-name>contextConfigLocation</param-name> 
      <param-value> /WEB-INF/security-config.xml </param-value> 
   </context-param> 
   <filter> 
      <filter-name>springSecurityFilterChain</filter-name> 
      <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 
   </filter> 
   <filter-mapping> 
      <filter-name>springSecurityFilterChain</filter-name> 
      <url-pattern>/*</url-pattern> 
   </filter-mapping> 
</web-app>

Code breakdown

  • Dispatcher Servlet − The first servlet we have declared here is the Dispatcher servlet. The dispatcher servlet is the entry point of any Spring MVC application and is at the core of the entire Spring MVC framework design. It intercepts all HTTP requests and dispatches them to registered handlers for processing a web request. It also provides convenient mapping and exception handling facilities. The order in which servlets are loaded depends on “load-on-startup” value. Servlets with a lower value of “load-on-startup” are loaded before the ones with a higher value.
  • contextConfigLocation − It is a string that indicates where context(s) can be found. This string represents a path to a file where our configurations can be loaded.
  • servlet-mapping − We use Servlet Mapping to tell Spring Container which request to route to which servlet. In our case, we are routing all our requests to our “spring” Dispatcher servlet.
  • listener − The classes that listen to certain types of events, and trigger an appropriate functionality when that event occurs. Each listener is bound to an event. In our case, we will create a root web-application context for the web-application with the ContextLoaderListener. This is then put in the ServletContext that can be used to load and unload the spring-managed beans.
  • filter − Spring uses Filters to process requests before handing them over to the Dispatcher Servlet and also used to process responses after they are dispatched. The DelegatingFilterProxy links the application context to the web.xml file. The requests that are coming to this application will pass through our filter which we named “spring SecurityFilterChain” before they reach their controllers. This is where Spring Security can take over the request and perform operations on it before passing it on to the next set of filters or handlers.

security-config.xml

Next we will create our security-config.xml file.

<?xml version="1.0" encoding="UTF-8"?> 
<beans:beans xmlns="http://www.springframework.org/schema/security" 
xmlns:beans="http://www.springframework.org/schema/beans" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd 
http://www.springframework.org/schema/security 
http://www.springframework.org/schema/security/spring-security.xsd"> 
<http auto-config="true"> 
<intercept-url pattern="/admin"
access="hasRole('ROLE_ADMIN')" /> </http> 
<authentication-manager> 
<authentication-provider> 
   <user-service> 
   <user name="admin" password="{noop}1234" authorities="ROLE_ADMIN" /> 
   </user-service> 
   </authentication-provider> </authentication-manager> 
   <beans:bean id ="passwordEncoder" 
      class = "org.springframework.security.crypto.password.NoOpPasswordEncoder" 
      factory-method = "getInstance">
   </beans:bean> 
</beans:beans>

Code breakdown

  • http element − The parent of all web-related namespace functionality. Here, we can configure which URLs to intercept, what permissions are required, which type of login to use, and all such configuration.
  • auto-config − Setting this attribute to true automatically sets up form-login, basic login, and logout functionalities. Spring Security generates them by using standard values and the features enabled.
  • intercept-url − It sets the pattern of the URLs that we want to protecte, using the access attribute.
  • access − It specifies which users are permitted to access the URL specified by the pattern attribute. It is done on the basis of the roles and permissions of a user. We can use SPEL with this attribute.
  • authentication-manager − The <authentication-manager> is used to configure users, their passwords, and roles in the application. These users will be one who can access the protected parts of the application given they have the appropriate roles. A DaoAuthenticationProvider bean will be created by the <authentication-provider< and the <user-service< element will create an InMemoryDaoImpl. All authentication-provider elements will allow the users to be authenticated by providing the user information to the authentication-manager.
  • password-encoder − This will register a password encoder bean. To keep things simple here we have used the NoOpPasswordEncoder.

Moving on we create out last configuration file – the app-config file. Here we are going to add our view resolver code and define our base package.

app-config.xml

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xmlns:mvc="http://www.springframework.org/schema/mvc" 
   xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" 
   http://www.springframework.org/schema/mvc 
   http://www.springframework.org/schema/mvc/spring-mvc.xsd 
   http://www.springframework.org/schema/beans 
   http://www.springframework.org/schema/beans/spring-beans.xsd 
   http://www.springframework.org/schema/context 
   http://www.springframework.org/schema/context/spring-context.xsd"> 
   <mvc:annotation-driven /> 
   <context:component-scan
      base-package="com.tutorial.spring.security.xmlconfigurationdemo.controller"> 
   </context:component-scan> 
   <context:annotation-config>
   </context:annotation-config> 
   <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 
      <property name="prefix" value="/WEB-INF/views/"></property> 
      <property name="suffix" value=".jsp"></property> 
   </bean> 
</beans>

Here, as we can see we are registering our views that we created earlier. For this, we are using the InternalResourceViewResolver class which will map the provided URI to the actual URI.

For example, using the above configuration, if we request the URI “/admin“, DispatcherServlet will forward the request to the

prefix + viewname + suffix = /WEB-INF/views/admin.jsp view.

Running the application

With this simple configuration, we have our application ready to be served. We can right-click on the project and choose Run on Server. We can choose our Tomcat server. When the server starts, we can go to localhost:8080/xmlconfigurationdemo to interact with our application.

If, we enter the correct credentials we shall be able to login and see our desired content.

Hello Admin

Spring Security - OAuth2

Contents

  • OAuth2.0 Fundamentals
  • OAuth2.0 Getting started(Practical Guide)

OAuth 2.0 Fundamentals

OAuth 2.0 Fundamentals

OAuth 2.0 was developed by IETF OAuth Working Group and published in October of 2012. It serves as an open authorization protocol for enabling a third party application to get limited access to an HTTP service on behalf of the resource owner. It can do so while not revealing the identity or the long-term credentials of the user. A third-party application itself can also use it on its behalf. The working principle of OAuth consists of the delegation of user authentication to a service hosting the user account and authorizing the third-party application access to the account of the user.

Let us consider an example. Let us say we want to login to a website “clientsite.com”. We can sign in via Facebook, Github, Google or Microsoft. We select any options of the options given above, and we are redirected to the respective website for login. If login is successful, we are asked if we want to give clientsite.com access to the specific data requested by it. We select our desired option and we are redirected to clientsite.com with an authorization code or error code and our login is successful or not depending on our action in the third-party resource. This is the basic working principle of OAuth 2.

There are five key actors involved in an OAuth system. Let’s list them out −

  • User / Resource Owner − The end-user, who is responsible for the authentication and for providing consent to share resources with the client.

  • User-Agent − The browser used by the User.

  • Client − The application requesting an access token.

  • Authorization Server − The server that is used to authenticate the user/client. It issues access tokens and tracks them throughout their lifetime.

  • Resource Server − The API that provides access to the requested resource. It validates the access tokens and provides authorization.

Getting Started

We will be developing a Spring Boot Application with Spring Security and OAuth 2.0 to illustrate the above. We will be developing a basic application with an in-memory database to store user credentials now. The application will make it easy for us to understand the workings of OAuth 2.0 with Spring Security.

Let’s use the Spring initializer to create a maven project in Java 8. Let’s start by going to start.spring.io. We generate an application with the following dependencies−

  • Spring Web
  • Spring Security
  • Cloud OAuth2
  • Spring Boot Devtools
Start Spring Project Metadata

With the above configuration, we click on the Generate button to generate a project. The project will be downloaded in a zip file. We extract the zip to a folder. We can then open the project in an IDE of our choice. I am using Spring Tools Suite here as it is optimized for spring applications. We can also use Eclipse or IntelliJ Idea as we wish.

So, we open the project in STS, let the dependencies get downloaded. Then we can see the project structure in our package explorer window. It should resemble the screenshot below.

Project in STS

If we open the pom.xml file we can view the dependencies and other details related to the project. It should look something like this.

<?xml version="1.0" encoding="UTF-8"?> 
<project xmlns="http://maven.apache.org/POM/4.0.0" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
   https://maven.apache.org/xsd/maven-4.0.0.xsd"> 
   <modelVersion>4.0.0</modelVersion> 
   <parent> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-parent</artifactId> 
      <version>2.3.1.RELEASE</version> 
      <relativePath/> <!-- lookup parent from repository -->
   </parent> 
   <groupId>com.tutorial</groupId> 
   <artifactId>spring.security.oauth2</artifactId> 
   <version>0.0.1-SNAPSHOT</version> 
   <name>spring.security.oauth2</name> 
   <description>Demo project for Spring Boot</description> 
   <properties> 
      <java.version>1.8</java.version> 
      <spring-cloud.version>Hoxton.SR6</spring-cloud.version> 
   </properties> 
   <dependencies> 
      <dependency> 
         <groupId>org.springframework.boot<groupId> 
         <artifactId>spring-boot-starter-security</artifactId> 
      </dependency> 
      <dependency> 
         <groupId>org.springframework.boot</groupId> 
         <artifactId>spring-boot-starter-web</artifactId> 
      </dependency> 
      <dependency>
         <groupId>org.springframework.cloud</groupId> 
         <artifactId>spring-cloud-starter-oauth2</artifactId> 
      </dependency> 
      <dependency> 
         <groupId>org.springframework.boot<groupId> 
         <artifactId>spring-boot-devtools</artifactId>
         <scope>runtime</scope> 
         <optional>true</optional> 
      </dependency> 
      <dependency> 
         <groupId>org.springframework.boot</groupId> 
         <artifactId>spring-boot-starter-test</artifactId> 
         <scope>test</scope> <exclusions>    <exclusion> 
            <groupId>org.junit.vintage</groupId> 
            <artifactId>junit-vintage-engine</artifactId> 
            </exclusion> 
         </exclusions> 
      <dependency> 
      <dependency>
         <groupId>org.springframework.security</groupId> 
         <artifactId>spring-security-test</artifactId> 
         <scope>test</scope> 
      </dependency> 
   </dependencies> 
      <dependencyManagement> 
   <dependencies> 
      <dependency> 
         <groupId>org.springframework.cloud</groupId> 
         <artifactId>spring-cloud-dependencies</artifactId> 
         <version>${spring-cloud.version}</version> 
         <type>pom</type> 
         <scope>import</scope> 
      </dependency> 
   </dependencies> 
   </dependencyManagement><build> 
   <plugins> 
      <plugin>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-maven-plugin</artifactId> 
      </plugin> 
   </plugins> 
   </build> 
</project>

Now, to the base package of our application, i.e., com.tutorial.spring.security.oauth2, let’s add a new package named config where we shall add our configuration classes.

Let’s create our first configuration class, UserConfig which extends the WebSecurityConfigurerAdapter class of Spring Security to manage the users of the client application. We annotate the class with @Configuration annotation to tell Spring that it is a configuration class.

package com.tutorial.spring.security.oauth2.config; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.security.authentication.AuthenticationManager; 
import org.springframework.security.config.annotation.web.builders.HttpSecurity; 
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 
import org.springframework.security.core.userdetails.User; 
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService; 
import org.springframework.security.crypto.password.NoOpPasswordEncoder; 
import org.springframework.security.crypto.password.PasswordEncoder; 
import org.springframework.security.provisioning.InMemoryUserDetailsManager; 
import org.springframework.security.provisioning.UserDetailsManager; 
@Configuration public class UserConfig extends WebSecurityConfigurerAdapter { 
   @Bean 
   public UserDetailsService userDetailsService() {
      UserDetailsManager userDetailsManager = new InMemoryUserDetailsManager(); 
      UserDetails user = User.withUsername("john") 
         .password("12345") .authorities("read") 
      .build(); userDetailsManager.createUser(user); return userDetailsManager; 
   } 
   @Bean
   public PasswordEncoder passwordEncoder() { 
      return NoOpPasswordEncoder.getInstance(); 
   } 
   @Override 
   @Bean 
   public AuthenticationManager authenticationManagerBean() throws Exception { 
      return super.authenticationManagerBean(); 
   } 
}

We then add a bean of the UserDetailsService to retrieve the user details for authentication and authorization. To put it in the Spring context we annotate it with @Bean. To keep this tutorial simple and easy to understand, we use an InMemoryUserDetailsManager instance. For a real-world application, we can use other implementations like JdbcUserDetailsManager to connect to a database and so on. To be able to create users easily for this example we use the UserDetailsManager interface which extends the UserDetailsService and has methods like createUser(), updateUser() and so on. Then, we create a user using the builder class. We give him a username, password and a “read” authority for now. Then, using the createUser() method, we add the newly created user and return the instance of UserDetailsManager thus putting it in the Spring context.

To be able to use the UserDetailsService defined by us, it is necessary to provide a PasswordEncoder bean in the Spring context. Again, to keep it simple for now we use the NoOpPasswordEncoder. The NoOpPasswordEncoder should not be used otherwise for real-world applications for production as it is not secure. NoOpPasswordEncoder does not encode the password and is only useful for developing or testing scenarios or proof of concepts. We should always use the other highly secure options provided by Spring Security, the most popular of which is the BCryptPasswordEncoder, which we will be using later in our series of tutorials. To put it in the Spring context we annotate the method with @Bean.

We then override the AuthenticationManager bean method of WebSecurityConfigurerAdapter, which returns the authenticationManagerBean to put the authentication manager into the Spring context.

Now, to add the client configurations we add a new configuration class named AuthorizationServerConfig which extends AuthorizationServerConfigurerAdapter class of Spring Security. The AuthorizationServerConfigurerAdapter class is used to configure the authorization server using the spring security oauth2 module. We annotate this class with @Configuration as well. To add the authorization server functionality to this class we need to add the @EnableAuthorizationServer annotation so that the application can behave as an authorization server.

package com.tutorial.spring.security.oauth2.config; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.security.authentication.AuthenticationManager; 
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; 
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; 
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; 
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; @Configuration @EnableAuthorizationServer 
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
   @Autowired private AuthenticationManager authenticationManager; 
   @Override 
   public void configure(ClientDetailsServiceConfigurer clients) throws Exception { 
      clients.inMemory() .withClient("oauthclient1") .secret("oauthsecret1") .scopes("read") .authorizedGrantTypes("password") } 
   @Override 
   public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { 
      endpoints.authenticationManager(authenticationManager); 
   } 
}

For checking oauth tokens, Spring Security oauth exposes two endpoints – /oauth/check_token and /oauth/token_key. These endpoints are protected by default behind denyAll(). tokenKeyAccess() and checkTokenAccess() methods open these endpoints for use.

We autowire the AuthenticationManager bean we configured in the UserConfig class as a dependency here which we shall be using later.

We then override two of the configure() methods of the AuthorizationServerConfigurerAdapter to provide an in-memory implementation of the client details service. The first method which uses the ClientDetailsServiceConfigurer as a parameter, as the name suggests, allows us to configure the clients for the authorization server. These clients represent the applications that will be able to use the functionality of this authorization server. Since this is a basic application for learning the implementation of OAuth2, we will keep things simple for now and use an in-memory implementation with the following attributes −

  • clientId − the id of the client. Required.

  • secret − the client secret, required for trusted clients

  • scope − the limiting scope of the client, in other words, client permissions. If left empty or undefined, the client is not limited by any scope.

  • authorizedGrantTypes − the grant types that the client is authorized to use. The grant type denotes the way by which the client obtains the token from the authorization server. We will be using the “password” grant type as it is the simplest. Later, we shall be using another grant type for another use-case.

In “password” authorization grant type, the user needs to provide his/her username, password and scope to our client application, which then uses those credentials along with its credentials for the authorization server we want the tokens from.

The other configure() method that we overrode, uses AuthorizationServerEndpointsConfigurer as a parameter, is used to attach the AuthenticationManager to authorization server configuration.

With these basic configurations, our Authorization server is ready to use. Let’s go ahead and start it and use it. We will be using Postman ( https://www.postman.com/downloads/ ) for making our requests.

When using STS, we can launch our application and start seeing see the logs in our console. When the application starts, we can find the oauth2 endpoints exposed by our application in the console. Of those endpoints, we will be using the following the below token for now −

/oauth/token – for obtaining the token.

Obtaining the Token

If we check the postman snapshot here, we can notice a few things. Let’s list them down below.

  • The URL − Our Spring Boot Application is running at port 8080 of our local machine, so the request is pointed to http://localhost:8080. The next part is /oauth/token, which we know, is the endpoint exposed by OAuth for generating the token.
  • The query params− Since this is a “password” authorization grant type, the user needs to provide his/her username, password and scope to our client application, which then uses those credentials along with its credentials to the authorization server we want the tokens from.
  • Client Authorization − The Oauth system requires the client to be authorized to be able to provide the token. Hence, under the Authorization header, we provide the client authentication information, namely username and password that we configured in our application.

Let’s take a closer look at the query params and the authorization header −

Authorization Header

The query params

Client Credentials

Client credentials

If everything is correct, we shall be able to see our generated token in the response along with a 200 ok status.

Response

The response

We can test our server, by putting wrong credentials or no credentials, and we will get back an error which would say the request is unauthorized or has bad credentials.

OAuth Authorization Server

This is our basic oauth authorization server, that uses the password grant type to generate and provide a password.

Next, let’s implement a more secure, and a more common application of the oauth2 authentication, i.e. with an authorization code grant type. We will update our current application for this purpose.

The authorization grant type is different from the password grant type in the sense that the user doesn’t have to share his credentials with the client application. He shares them with the authorization server only and in return authorization code is sent to the client which it uses to authenticate the client. It is more secure than the password grant type as user credentials are not shared with the client application and hence the user’s information stays safe. The client application doesn’t get access to any important user information unless approved by the user.

In a few simple steps, we can set up a basic oauth server with an authorization grant type in our application. Let’s see how.

package com.tutorial.spring.security.oauth2.config; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; 
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; 
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; 
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; 
@Configuration 
@EnableAuthorizationServer 
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { 
   @Autowired private AuthenticationManager authenticationManager; 
   @Override 
   public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
      clients.inMemory()       
      .withClient("oauthclient1")   
      .secret("oauthsecret1")
      .scopes("read") .authorizedGrantTypes("password") 
      .and() .withClient("oauthclient2") .secret("oauthsecret2") 
      .scopes("read") .authorizedGrantTypes("authorization_code") 
      .redirectUris("http://locahost:9090"); 
   }
   @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { 
      endpoints.authenticationManager(authenticationManager); 
   } 
}

Let’s add a second client for this operation oauthclient2 for this operation with a new secret and read scope. Here we have changed the grant type to authorization code for this client. We also added a redirect URI so that the authorization server can callback the client. So, basically the redirect URI is the URI of the client.

Now, we have to establish a connection between the user and the authorization server. We have to set an interface for the authorization server where the user can provide the credentials. We use the formLogin() implementation of Spring Security to achieve that functionality while keeping things simple. We also make sure that all requests are authenticated.

package com.tutorial.spring.security.oauth2.config; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.security.authentication.AuthenticationManager; 
import org.springframework.security.config.annotation.web.builders.HttpSecurity; 
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 
import org.springframework.security.core.userdetails.User; 
import org.springframework.security.core.userdetails.UserDetails; 
import org.springframework.security.core.userdetails.UserDetailsService; 
import org.springframework.security.crypto.password.NoOpPasswordEncoder; 
import org.springframework.security.crypto.password.PasswordEncoder; 
import org.springframework.security.provisioning.InMemoryUserDetailsManager; 
import org.springframework.security.provisioning.UserDetailsManager; 
@SuppressWarnings("deprecation") @Configuration 
public class UserConfig extends WebSecurityConfigurerAdapter {
   @Bean
   public UserDetailsService userDetailsService() {
      UserDetailsManager userDetailsManager = new InMemoryUserDetailsManager(); 
         UserDetails user = User.withUsername("john") 
      .password("12345") .authorities("read") .build(); 
      userDetailsManager.createUser(user); return userDetailsManager; 
   } 
   @Bean public PasswordEncoder passwordEncoder() { 
      return NoOpPasswordEncoder.getInstance(); 
    } 
   @Override 
   @Bean 
   public AuthenticationManager authenticationManagerBean() throws Exception {
      return super.authenticationManagerBean(); 
   }
   @Override protected void configure(HttpSecurity http) throws Exception {
      http.formLogin(); http.authorizeRequests().anyRequest().authenticated(); 
   } 
}

This completes our setup for the authorization grant type. Now to test our setup and launch our application. We launch our browser at http://localhost:8080/oauth/authorize?response_type=code&client_id=oauthclient2&scope=read. We will redirected to the default form login page of Spring Security.

OAuth Authorization Server Signin

Here, the response type code implies that the authorization server will return an access code which will be used by the client to log in. When we use the user credentials we will be asked if I want to grant the permissions asked by the client, in a similar screen as shown below.

OAuth Approval

If we approve and click Authorize we shall see we are redirected to our given redirect url along with the access code. In our case the we are redirected to http://locahost:9090/?code=7Hibnw, as we specified in the application. We can use the code now as a client in Postman to login to the authorization server.

Postman Authorization

As we can see here, we have used the code received from the authorization server in our URL, and the grant_type as authorization_code and scope as read. We acted as the client and provided the client credentials as configured in our application. When we make this request we get back our access_token which we can use further.

So, we have seen how we can configure Spring Security with OAuth 2.0. The application is pretty simple and easy to understand and helps us understand the process fairly easily. We have used two kinds of authorization grant types and seen how we can use them to acquire access tokens for our client application.

Spring Security - JWT

Contents

  • JWT Introduction and overview
  • Getting started with Spring Security using JWT(Practical Guide)

JWT Introduction and overview

JSON Web Token or JWT, as it is more commonly called, is an open Internet standard (RFC 7519) for securely transmitting trusted information between parties in a compact way. The tokens contain claims that are encoded as a JSON object and are digitally signed using a private secret or a public key/private key pair. They are self-contained and verifiable as they are digitally signed. JWT’s can be signed and/or encrypted. The signed tokens verify the integrity of the claims contained in the token, while the encrypted ones hide the claims from other parties.

JWT’s can also be used for the exchange of information though they more commonly used for authorization as they offer a lot of advantages over session management using in-memory random tokens. The biggest of them being the enabling the delegation of authentication logic to a third-party server like AuthO etc.

A JWT token is divided into 3 parts namely – header, payload, and signature in the format of

[Header].[Payload].[Signature]
  • Header − The Header of a JWT token contains the list cryptographic operations that are applied to the JWT. This can be the signing technique, metadata information about the content-type and so on. The header is presented as a JSON object which is encoded to a base64URL. An example of a valid JWT header would be

{ "alg": "HS256", "typ": "JWT" }

Here, “alg” gives us information about the type of algorithm used and “typ gives us the type of the information.

  • Payload − The payload part of JWT contains the actual data to be transferred using the token. This part is also known as the “claims” part of the JWT token. The claims can be of three types – registered, public and private.

  • The registered claims are the ones which are recommended but not mandatory claims such as iss(issuer), sub(subject), aud(audience) and others.

  • Public claims are those that are defined by those using the JWTs.

  • Private claims or custom claims are user-defined claims created for the purpose of sharing the information between the concerned parties.

Example of a payload object could be.

{ "sub": "12345", "name": "Johnny Hill", "admin": false }

The payload object, like the header object is base64Url encoded as well and this string forms the second part of the JWT.

  • Signature− The signature part of the JWT is used for the verification that the message wasn’t changed along the way. If the tokens are signed with private key, it also verifies that the sender is who it says it is. It is created using the encoded header, encoded payload, a secret and the algorithm specified in the header. An example of a signature would be.

HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

If we put the header, payload and signature we get a token as given below.

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6I
kpvaG4gRG9lIiwiYWRtaW4iOmZhbHNlfQ.gWDlJdpCTIHVYKkJSfAVNUn0ZkAjMxskDDm-5Fhe
WJ7xXgW8k5CllcGk4C9qPrfa1GdqfBrbX_1x1E39JY8BYLobAfAg1fs_Ky8Z7U1oCl6HL63yJq_
wVNBHp49hWzg3-ERxkqiuTv0tIuDOasIdZ5FtBdtIP5LM9Oc1tsuMXQXCGR8GqGf1Hl2qv8MCyn
NZJuVdJKO_L3WGBJouaTpK1u2SEleVFGI2HFvrX_jS2ySzDxoO9KjbydK0LNv_zOI7kWv-gAmA
j-v0mHdJrLbxD7LcZJEGRScCSyITzo6Z59_jG_97oNLFgBKJbh12nvvPibHpUYWmZuHkoGvuy5RLUA

Now, this token can be used in the Authorization header using the Bearer schema as.

Authorization − Bearer <token>

The use of JWT token for authorization is the most common of its applications. The token is usually generated in the server and sent to the client where it is stored in the session storage or local storage. To access a protected resource the client would send the JWT in the header as given above. We will see the JWT implementation in Spring Security in the section below.

Getting Started with Spring Security using JWT

The application we are going to develop will handle basic user authentication and authorization with JWT’s. Let’s get started by going to start.spring.io where we will create a Maven application with the following dependencies.

  • Spring Web
  • Spring Security
Maven Project Java

We generate the project and when it is downloaded, we extract it to a folder of our choice. We can then use any IDE of our choice. I am going to use Spring Tools Suite 4 as it is most optimized for Spring applications.

Apart from the above-mentioned dependencies we are also going to include the jwt dependency from io.jsonwebtoken from the Maven central repository as it is not included in the spring initializer. This dependency takes care of all operations involving the JWT including building the token, parsing it for claims and so on.

<dependency> 
   <artifactId>jjwt</artifactId> 
   <version>0.9.1</version> 
</dependency>

Our pom.xml file should now look similar to this.

<?xml version="1.0" encoding="UTF-8"?> 
<project xmlns="http://maven.apache.org/POM/4.0.0" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
   https://maven.apache.org/xsd/maven-4.0.0.xsd"> 
   <modelVersion>4.0.0</modelVersion> 
   <parent> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-parent</artifactId> 
      <version>2.3.1.RELEASE<version> 
      <relativePath /> 
      <!-- lookup parent from repository --> 
   </parent> 
   <groupId>com.spring.security</groupId> 
   <artifactId>jwtbasic</artifactId>
   <version>0.0.1-SNAPSHOT</version> 
   <name>jwtbasic</name> 
   <description>Demo project for Spring Boot</description> 
   <properties> 
      <java.version>1.8</java.version> 
   </properties> 
   <dependencies> 
      <dependency> 
         <groupId>org.springframework.boot</groupId> 
         <artifactId>spring-boot-starter-security</artifactId> 
      </dependency> 
      <dependency>
         <groupId>org.springframework.boot</groupId> 
         <artifactId>spring-boot-starter-web</artifactId> 
      </dependency> 
      <dependency> 
         <groupId>io.jsonwebtoken</groupId> 
         <artifactId>jjwt</artifactId> 
         <version>0.9.1</version> 
      </dependency>
      <dependency> 
         <groupId>javax.xml.bind</groupId> 
         <artifactId>jaxb-api</artifactId> 
      </dependency> 
      <dependency> 
         <groupId>org.springframework.boot</groupId> 
         <artifactId>spring-boot-starter-test</artifactId> 
         <scope>test</scope> 
         <exclusions> 
      <exclusion> 
         <groupId>org.junit.vintage</groupId> 
         <artifactId>junit-vintage-engine</artifactId> 
         </exclusion> 
      </exclusions> 
      </dependency> 
      <dependency> 
      <groupId>org.springframework.security</groupId> 
      <artifactId>spring-security-test</artifactId> 
      <scope>test<scope>
      <dependency> 
   </dependencies> 
      <build> 
      <plugins> 
         <plugin> 
         <groupId>org.springframework.boot</groupId> 
         <artifactId>spring-boot-maven-plugin</artifactId> 
         </plugin> 
      </plugins> 
   </build> 
</project>

Now that our project is set up we are going to create our controller class Hello Controller which exposes a Get endpoint.

package com.spring.security.jwtbasic.controllers; 
import org.springframework.web.bind.annotation.GetMapping; 
import org.springframework.web.bind.annotation.RestController; 
@RestController 
public class HelloController {
   @GetMapping("/hello") 
   public String hello() { 
      return "hello"; 
   } 
}

Now we are going to create a package called config where we add the configuration class that extends the WebSecurityConfigurerAdapter class of Spring Security. This will provide us with all the required functions and definitions for project configuration and security of our application. For now, we provide the BcryptPasswordEncoder instance by implementing a method that generates the same. We annotate the method with @Bean to add to our Spring Context.

package com.spring.security.jwtbasic.config; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.security.authentication.AuthenticationManager; 
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 
import org.springframework.security.config.annotation.web.builders.HttpSecurity; 
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService; 
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 
import org.springframework.security.crypto.password.PasswordEncoder; 
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 
import com.spring.security.jwtbasic.jwtutils.JwtAuthenticationEntryPoint; 
import com.spring.security.jwtbasic.jwtutils.JwtFilter; 
@Configuration 
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
   @Bean 
   public PasswordEncoder passwordEncoder() { 
      return new BCryptPasswordEncoder(); 
   } 
}

The JWT includes a secret which we will define in our application.properties file as given below.

secret=somerandomsecret

Now let’s create a package called jwtutils. This package is going to contain all classes and interface related to JWT operations, which will include.

  • Generating token
  • Validating token
  • Checking the signature
  • Verifying claims and permissions

In this package, we create our first class called Token Manager. This class will be responsible for the creation and validation of tokens using io.jsonwebtoken.Jwts.

package com.spring.security.jwtbasic.jwtutils; 
import java.io.Serializable; 
import java.util.Base64; 
import java.util.Date; 
import java.util.HashMap; 
import java.util.Map; 
import org.springframework.beans.factory.annotation.Value; 
import org.springframework.security.core.userdetails.UserDetails; 
import org.springframework.stereotype.Component; 
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; 
import io.jsonwebtoken.SignatureAlgorithm; 
@Component 
public class TokenManager implements Serializable {
   /** 
   *
   */ 
   private static final long serialVersionUID = 7008375124389347049L; public static final long TOKEN_VALIDITY = 10 * 60 * 60; @Value("${secret}") 
   private String jwtSecret; 
   public String generateJwtToken(UserDetails userDetails) { 
      Map<String, Object> claims = new HashMap<>(); 
      return Jwts.builder().setClaims(claims).setSubject(userDetails.getUsername()) 
         .setIssuedAt(new Date(System.currentTimeMillis())) 
         .setExpiration(new Date(System.currentTimeMillis() + TOKEN_VALIDITY * 1000)) 
         .signWith(SignatureAlgorithm.HS512, jwtSecret).compact(); 
   } 
   public Boolean validateJwtToken(String token, UserDetails userDetails) { 
      String username = getUsernameFromToken(token); 
      Claims claims = Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody();
      Boolean isTokenExpired = claims.getExpiration().before(new Date()); 
      return (username.equals(userDetails.getUsername()) && !isTokenExpired); 
   } 
   public String getUsernameFromToken(String token) {
      final Claims claims = Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody(); 
      return claims.getSubject(); 
   } 
}

Here, as all tokens should have an expiration date, we start with a token validity constant. Here, we want our token to be valid for 10 minutes after generation. We will use this value when we generate our token. Then we extract the value of our singing key from the application.properties file into our jwtSecret field using the @Value annotation.

We have two methods here −

  • generateJwtToken() − This method is used to generate a token on successful authentication by the user. To create the token here we use the username, issue date of token and the expiration date of the token. This will form the payload part of the token or claims as we had discussed earlier. To generate the token we use the builder() method of Jwts. This method returns a new JwtBuilder instance that can be used to create compact JWT serialized strings.

To set the claims we use the setClaims() method and then set each of the claims. For this token we have setSubject(username), issue date and expiration date. We can also put our custom claims as we had discussed above. This can be any value we want which might include user role, user authorities and so on.

Then we set the signature part of the token. This is done using the signWith() method, we set the hashing algorithm we prefer to use and the secret key. Then we use thecompact() method that builds the JWT and serializes it to a compact, URL-safe string according to the JWT Compact Serialization rules.

  • validateJwtToken() − Now that the generation of the token is taken care of, we should focus on the process of validation of the token when it comes as a part of requests. To validate the token means to verify the request is an authenticated one and that the token is the one that was generated and sent to the user. Here, we need to parse the token for the claims such as username, roles, authorities, validity period etc.

To validate the token we need to parse it first. This is done using the parser() method of Jwts. We then need to set the signing key that we used to generate the token and then use parseClaimsJws() method on the token to parse the compact serialized JWS string based on the builder’s current configuration state and return the resulting Claims JWS instance. The getBody() method is then used to return the claims instance that was used while generating the token.

From this obtained claims instance, we extract the subject and the expiry date to verify the validity of the token. The username should be the username of the user and the token should not be expired. If both these conditions are met, we return true, which signifies that the token is valid.

The next class we would be creating is the JwtUserDetailsService. This class will extend the UserDetailsService of Spring security and we will implement the loadUserByUsername() method as given below −

package com.spring.security.jwtbasic.jwtutils; 
import java.util.ArrayList; 
import org.springframework.security.core.userdetails.User; 
import org.springframework.security.core.userdetails.UserDetails; 
import org.springframework.security.core.userdetails.UserDetailsService; 
import org.springframework.security.core.userdetails.UsernameNotFoundException; 
import org.springframework.stereotype.Service; 
@Service
public class JwtUserDetailsService implements UserDetailsService { 
   @Override 
   public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
      if ("randomuser123".equals(username)) { 
         return new User("randomuser123", 
            "$2a$10$slYQmyNdGzTn7ZLBXBChFOC9f6kFjAqPhccnP6DxlWXx2lPk1C3G6", 
            new ArrayList<>()); 
      } else { 
         throw new UsernameNotFoundException("User not found with username: " + username); 
      } 
   } 
}

Here, since this is a basic application for the sole purpose of the demonstration of JWT authentication, we have resorted to a set of our user details, instead of using a database. We have given the username as “randomuser123” and encoded the password, which is “password” as “$2a$10$slYQmyNdGzTn7ZLBXBChFOC9f6kFjAqPhccnP6DxlWXx2lPk1C3G6” for our convenience.

Next, we create classes for our Request and Response models. These models determine how our request and response formats would be for authentication. The first snapshot given below is the request model. As we can see, we shall be accepting two properties – username and password in our request.

package com.spring.security.jwtbasic.jwtutils.models;
import java.io.Serializable; 
public class JwtRequestModel implements Serializable { 
   /** 
   * 
   */ 
   private static final long serialVersionUID = 2636936156391265891L; 
   private String username; 
   private String password; 
   public JwtRequestModel() { 
   } 
   public JwtRequestModel(String username, String password) { 
      super(); 
      this.username = username; this.password = password; 
   } 
   public String getUsername() { 
      return username;
   } 
   public void setUsername(String username) { 
      this.username = username; 
   } 
   public String getPassword() { 
      return password; 
   } 
   public void setPassword(String password) { 
      this.password = password; 
   } 
}

Below is the code for response model on successful authentication. As we can see, we will be sending the token back to the user on successful authentication.

package com.spring.security.jwtbasic.jwtutils.models; 
import java.io.Serializable; 
public class JwtResponseModel implements Serializable {
   /**
   *
   */
   private static final long serialVersionUID = 1L;
   private final String token;
   public JwtResponseModel(String token) {
      this.token = token;
   }
   public String getToken() {
      return token;
   }
}

For authentication now, let’s create a controller as given below.

package com.spring.security.jwtbasic.jwtutils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.spring.security.jwtbasic.jwtutils.models.JwtRequestModel;
import com.spring.security.jwtbasic.jwtutils.models.JwtResponseModel;
@RestController
@CrossOrigin
public class JwtController {
   @Autowired
   private JwtUserDetailsService userDetailsService;
   @Autowired
   private AuthenticationManager authenticationManager;
   @Autowired
   private TokenManager tokenManager;
   @PostMapping("/login")
   public ResponseEntity<> createToken(@RequestBody JwtRequestModel
   request) throws Exception {
      try {
         authenticationManager.authenticate(
            new
            UsernamePasswordAuthenticationToken(request.getUsername(),
            request.getPassword())
         );
      } catch (DisabledException e) {
         throw new Exception("USER_DISABLED", e);
      } catch (BadCredentialsException e) {
         throw new Exception("INVALID_CREDENTIALS", e);
      }
      final UserDetails userDetails = userDetailsService.loadUserByUsername(request.getUsername());
      final String jwtToken = tokenManager.generateJwtToken(userDetails);
      return ResponseEntity.ok(new JwtResponseModel(jwtToken));
   }
}

If we go through the code we can see that, we have autowired three dependencies namely, JwtUserDetailsService, AuthenticationManager and TokenManager. While we have already seen the implementation of JwtUserDetailsService and TokenManager classes above, the authentication manager bean is one we shall be creating in our WebSecurityConfig class.

AuthenticationManager class will take care of our authentication. We shall be using the UsernamePasswordAuthenticationToken model for authentication of the request. If authentication succeeds we shall generate a JWT for the user, which can be sent in the Authorization header of the subsequent requests to get any resource.

As we can see, we are using the loadUserByUsername() method of our JwtUserDetailsService class and the generateJwtToken() from TokenManager class.

This generated JWT is sent to the user as a response on successful authentication as mentioned above.

Now it’s time we created our Filter. The filter class will be used to track our requests and detect if they contain the valid token in the header. If the token is valid we let the request proceed otherwise we send a 401 error (Unauthorized).

package com.spring.security.jwtbasic.jwtutils;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import io.jsonwebtoken.ExpiredJwtException;
@Component
public class JwtFilter extends OncePerRequestFilter {
   @Autowired
   private JwtUserDetailsService userDetailsService;
   @Autowired
   private TokenManager tokenManager;
   @Override
   protected void doFilterInternal(HttpServletRequest request,
      HttpServletResponse response, FilterChain filterChain)
      throws ServletException, IOException {
      
      String tokenHeader = request.getHeader("Authorization");
      String username = null;
      String token = null;
      if (tokenHeader != null && tokenHeader.startsWith("Bearer ")) {
         token = tokenHeader.substring(7);
         try {
            username = tokenManager.getUsernameFromToken(token);
         } catch (IllegalArgumentException e) {
            System.out.println("Unable to get JWT Token");
         } catch (ExpiredJwtException e) {
            System.out.println("JWT Token has expired");
         }
      } else {
         System.out.println("Bearer String not found in token");
      }
      if (null != username &&SecurityContextHolder.getContext().getAuthentication() == null) {
         UserDetails userDetails = userDetailsService.loadUserByUsername(username);
         if (tokenManager.validateJwtToken(token, userDetails)) {
            UsernamePasswordAuthenticationToken
            authenticationToken = new UsernamePasswordAuthenticationToken(
            userDetails, null,
            userDetails.getAuthorities());
            authenticationToken.setDetails(new
            WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
         }
      }
      filterChain.doFilter(request, response);
   }
}

As we can see above, we have autowired the JwtUserDetailsService and TokenManager classes here as well. We have extended the OncePerRequestFilter of SpringSecurity which makes sure the filter is run for every request. We have provided our implementation to the overridden method doFilterInternal() of the OncePerRequestFilter class.

The method here extracts the token from the header and validates it with the help of validateJwtToken() method of our TokenManager class. During validation, it checks for the username and the expiration date. If both the values are valid, we save the authentication in our Spring Security context and let the code proceed to the next filter in our filter chain. If any of the validation fails or there is an issue with the token or if the token is not found we throw the appropriate exceptions and send back an appropriate response while blocking the request from moving ahead.

Having created the filter for our requests, we now create the JwtAutheticationEntryPoint class. This class extends Spring’s AuthenticationEntryPoint class and rejects every unauthenticated request with an error code 401 sent back to the client. We have overridden the commence() method of AuthenticationEntryPoint class to do that.

package com.spring.security.jwtbasic.jwtutils;
import java.io.IOException;
import java.io.Serializable;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint,
Serializable {
   @Override
   public void commence(HttpServletRequest request, HttpServletResponse
   response,
   AuthenticationException authException) throws
   IOException, ServletException {
      response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
   "Unauthorized");
      }
}

Now, let’s get back to our WebSecurityConfig class and finish the rest of our configuration. If we remember, we are going to require our AuthenticationManager bean for our Jwt controller class and add the filter we just created to our configuration. We are also going to configure which requests are to be authenticated and which are not to be. We shall also add the AuthenticationEntryPoint to our requests to send back the 401 error response. Since, we also do not need to maintain session variables while using jwt we can make our session STATELESS.

package com.spring.security.jwtbasic.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.spring.security.jwtbasic.jwtutils.JwtAuthenticationEntryPoint;
import com.spring.security.jwtbasic.jwtutils.JwtFilter;
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
   @Autowired
   private JwtAuthenticationEntryPoint authenticationEntryPoint;
   @Autowired
   private UserDetailsService userDetailsService;
   @Autowired
   private JwtFilter filter;
   @Bean
   public PasswordEncoder passwordEncoder() {
      return new BCryptPasswordEncoder();
   }
   @Override
   protected void configure(AuthenticationManagerBuilder auth) throws Exception {
      auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
   }
   @Bean
   @Override
   public AuthenticationManager authenticationManagerBean() throws
   Exception {
      return super.authenticationManagerBean();
   }
   @Override
   protected void configure(HttpSecurity http) throws Exception {
      http.csrf().disable()
      .authorizeRequests().antMatchers("/login").permitAll()
      .anyRequest().authenticated()
      .and()
      .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
      .and()
      .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
      http.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class);
   }
}

As we can see, we have done all of that, and now our application is ready to go. Let’s start the application and use postman for making our requests.

Postman Body

Here we have made our first request to get the token, and as we can see on providing the correct username/password combination we get back our token.

Now using that token in our header, let’s call the /hello endpoint.

Postman Authorization Body

As we can see, since the request is authenticated, we get the desired response back. Now, if we tamper with the token or do not send the Authorization header, we will get a 401 error as configured in our application. This ensures that the protection our request using the JWT.

Advertisements