Spring Boot Security – Form Based Authentication – Persistence Token – Remember Me

Introduction

The example Spring Boot Security form based authentication persistence token remember me will show you how to use custom login form with Spring’s j_spring_security_check to authenticate a user. You may also look into form based authentication remember me – persistent token – on Spring MVC framework. The similar example we will implement here but using Spring Boot framework. We will add additional field as remember me into the login form.

This tutorial will show you how to remember your credentials for a specific time period for auto-login without providing any login credentials into the login form.

Remember-me or persistent-login authentication refers to web sites being able to remember the identity of a principal between sessions.

This is typically accomplished by sending a cookie to the browser, with the cookie being detected during future sessions and causing automated login to take place.

Spring Security provides the necessary hooks for these operations to take place, and has two concrete remember-me implementations. One uses hashing to preserve the security of cookie-based tokens and the other uses a database or other persistent storage mechanism to store the generated tokens.

This example uses database storage to store generated tokens for Remember Me login implementation as opposed to the tutorial, where cookie based token approach was achieved.

We will use here JDBC authentication, you can check my other Spring Security tutorials.

Prerequisites

Eclipse Neon, Java 1.8, Gradle 5.4.1, Spring Boot 2.1.6

Read tutorial Spring Boot Security Form based JDBC Authentication before proceeding below sections.

Adding SQL Script

We are not going to tell you from the initial project creation but we will modify the existing application as we asked you to read tutorial before coming here.

You may also download the complete source code later from this tutorial.

We need to add a SQL script to create a table for storing the token into database.

Create a file called persistent_logins.sql in addition to other SQL scripts under src/main/resources folder with the following content.

DROP TABLE IF EXISTS `persistent_logins`;
CREATE TABLE `persistent_logins` (
  `username` varchar(64) NOT NULL,
  `series` varchar(64) NOT NULL,
  `token` varchar(64) NOT NULL,
  `last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`series`)
);

Updating DatabaseConfig Class

Need to define additional bean for persistence token approach. The token will be saved into database and will be checked for authentication for remember me option.

@Bean
public PersistentTokenRepository persistentTokenRepository() {
	JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
	tokenRepository.setDataSource(dataSource());
	return tokenRepository;
}

The complete source code for DatabaseConfig.java file is given below:

package com.roytuts.spring.boot.security.form.auth.remember.persistence.token.config;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
@Configuration
public class DatabaseConfig {
	@Bean
	public DataSource dataSource() {
		EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
		EmbeddedDatabase db = builder.setType(EmbeddedDatabaseType.H2) // .H2 or .DERBY, etc.
				.addScript("user.sql").addScript("user-role.sql").addScript("persistent_logins.sql").build();
		return db;
	}
	@Bean
	public PersistentTokenRepository persistentTokenRepository() {
		JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
		tokenRepository.setDataSource(dataSource());
		return tokenRepository;
	}
}

Updating SecurityConfig Class

The file is quite self-explanation and we have added the following code snippets in addition to the existing code base.

.and().rememberMe()// remember me,
				.key("rememberKey")// key for remember me,
				.rememberMeParameter("remember")// remember me field name in login form,
				.tokenValiditySeconds(86400);// remember me for one day

The updated SecurityConfig.java file is shown below:

package com.roytuts.spring.boot.security.form.auth.remember.persistence.token.config;
import javax.sql.DataSource;
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.authentication.builders.AuthenticationManagerBuilder;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	@Autowired
	private DataSource dataSource;
	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
		final String sqlUserName = "select u.user_name, u.user_pass, u.enable from user u where u.user_name = ?";
		final String sqlAuthorities = "select ur.user_name, ur.user_role from user_role ur where ur.user_name = ?";
		auth.jdbcAuthentication().dataSource(dataSource).usersByUsernameQuery(sqlUserName)
				.authoritiesByUsernameQuery(sqlAuthorities).passwordEncoder(passwordEncoder());
	}
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http// ,
				.authorizeRequests()// , authorize request
				.antMatchers("/", "/login", "/static/**", "/error**").permitAll().anyRequest().authenticated()// ,
																												// ignore
																												// /,login
																												// page,static
				// resources, error
				// pages
				.antMatchers("/admin")// Ensures that request with "/admin" to
										// our application requires the user to
										// be authenticated
				.access("hasRole('ADMIN')")// Any URL that starts with
											// "/admin" will
				// be restricted to users who have the
				// role "ROLE_ADMIN",
				.and()// ,
				.formLogin()// Allows users to authenticate with form based
							// login,
				.loginPage("/login")// specifies the location of the log in
									// page,
				.loginProcessingUrl("/j_spring_security_check")// login
																// processing
																// URL,
				.defaultSuccessUrl("/admin")// default-target-url,
				.failureUrl("/login?error")// authentication-failure-url,
				.usernameParameter("username")// overrides Spring's default
												// j_username with
												// username-parameter,
				.passwordParameter("password")// overrides Spring's default
												// j_password with
												// password-parameter,
				.and().rememberMe()// remember me,
				.key("rememberKey")// key for remember me,
				.rememberMeParameter("remember")// remember me field name in login form,
				.tokenValiditySeconds(86400);// remember me for one day
	}
	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
}

The above configuration is persistent token approach and this approach is more robust as opposed to simple hash based token approach, which is exposed to a security threat, because a captured remember-me token will be usable from any user agent until such time as the token expires and is usually not recommended.

Updating Login Form

We will use the existing login.jsp page and update the login form to include additional checkbox for remember me option.

The following code snippets have been added to the login form.

<tr>
	<td>Remember Me:</td>
	<td><input type='checkbox' name="remember" /></td>
</tr>

We provided an option, usually checkbox, to the user to select Remember Me and if a user checks it then after successful login, spring application sends a remember-me cookie to the browser in addition to session cookie. Once the session cookie is expired, then if user accesses the secure page, it will automatically be logged-in using remember-me cookie.

Deploying and Testing Application

Deploying and testing application is same as Spring Boot Security Form based JDBC Authentication.

Now when you try to access the admin page you will be redirected to login page and you will see the Remember Mecheckbox for next time auto-login.

spring boot security form based authentication persistence token remember me

So once you login with checkbox checked and next time when you hit the direct URL http://localhost:8080/admin into the browser you will automatically be redirected to the admin page and you are no longer required to login using credentials.

Or even if you click on the link Go to Administrator page from home page, you will be automatically redirected to the admin page.

You will also find entry into the database table with token value.

Source Code

download source code

Thanks for reading.

Leave a Reply

Your email address will not be published. Required fields are marked *