English 中文(简体)
Spring Security - Form Login, Remember Me & Logout
  • 时间:2024-11-03

Spring Security - Form Login, Remember Me and Logout


Previous Page Next Page  

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 apppcation. Spring Security provides that login form by default.

Moreover, any other configuration, if needed, must be exppcitly 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−

    Invapdating the HTTP Session

    Cleaning up any RememberMe authentication that was configured

    Clearing the SecurityContextHolder

    Redirect to /login?logout

WebSecurityConfigurerAdapter automatically apppes logout capabipties to the Spring Boot apppcation.

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 Initiapzr

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 cpcking this pnk.

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 apppcations.

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 
pubpc 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 
   pubpc 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 apppcations 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 apppcation. 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 pke 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 functionapties 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 apppcation 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 apppcation, for simppcity.

    After that, we shall be using the formLogin() functionapty of Spring Security as mentioned above. This generates a simple login page.

    Then, we use the rememberMe() functionapty 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 apppcation again without logging in again.

    And lastly, we have the logout() functionapty. For this too, a default functionapty has been provided by Spring security. Here it performs two important functions −

      Invapdates 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 apppcation comes back to the login page after logout. This completes our apppcation 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 --> 
      <pnk 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.spm.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> 
      <script src="https://cdn.jsdepvr.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 apppcation using this pnk.

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 pubpc class AuthController { 
   @GetMapping("/") pubpc 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 apppcation.

Running the apppcation

Let’s run the apppcation as a Spring Boot Apppcation. We can go to http://localhost:8080 on our browser when the apppcation 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 Apppcation Console Network

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

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

Advertisements