In order to remember the users on the computer while logging into a website, via Spring Security we can maintain that by using a “Remember Me” option. Spring security sends a cookie to the browser. This will have a specific time limit and till that time the cookie is valid and in that timeframe, for the given URL, automatic login is possible. Let us see via a sample application code to see the working functionality.
Example Project
Spring Security is providing two approaches for implementing remember-me
- Hash-Based Token Approach
- Persistent Token Approach.
In our example, we are taking the Persistent Token Approach in which a database or other persistent storage mechanism is used and it is helpful to store the generated tokens.
Project Structure:
This is a maven-driven project. Let us see the pom.xml
XML
< modelVersion >4.0.0</ modelVersion >
< groupId >com.gfg.springsecurity</ groupId >
< artifactId >spring-security-remember-me-sample-application</ artifactId >
< version >0.0.1-SNAPSHOT</ version >
< name >spring-security-remember-me-sample-application</ name >
< packaging >war</ packaging >
< properties >
< maven.compiler.source >1.8</ maven.compiler.source >
< maven.compiler.target >1.8</ maven.compiler.target >
< failOnMissingWebXml >false</ failOnMissingWebXml >
</ properties >
< dependencies >
< 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-config</ artifactId >
< version >5.0.0.RELEASE</ version >
</ dependency >
< dependency >
< groupId >org.springframework</ groupId >
< artifactId >spring-webmvc</ artifactId >
< version >5.0.2.RELEASE</ version >
</ dependency >
< dependency >
< groupId >org.springframework</ groupId >
< artifactId >spring-jdbc</ artifactId >
< version >5.0.2.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.jsp</ groupId >
< artifactId >javax.servlet.jsp-api</ artifactId >
< version >2.3.1</ version >
< scope >provided</ scope >
</ dependency >
< dependency >
< groupId >javax.servlet.jsp.jstl</ groupId >
< artifactId >javax.servlet.jsp.jstl-api</ artifactId >
< version >1.2.1</ version >
</ dependency >
< dependency >
< groupId >taglibs</ groupId >
< artifactId >standard</ artifactId >
< version >1.1.2</ version >
</ dependency >
< dependency >
< groupId >org.apache.commons</ groupId >
< artifactId >commons-dbcp2</ artifactId >
< version >2.1.1</ version >
</ dependency >
< dependency >
< groupId >mysql</ groupId >
< artifactId >mysql-connector-java</ artifactId >
< version >6.0.6</ version >
</ dependency >
</ dependencies >
< build >
< plugins >
< plugin >
< groupId >org.eclipse.jetty</ groupId >
< artifactId >jetty-maven-plugin</ artifactId >
< version >9.4.8.v20171121</ version >
</ plugin >
< plugin >
< groupId >org.apache.maven.plugins</ groupId >
< artifactId >maven-war-plugin</ artifactId >
< version >3.3.1</ version >
</ plugin >
</ plugins >
</ build >
</ project >
|
On the database side, we are using MySQL. Hence those configurations are given in pom.xml. To test the application for remembering, we need to have some sample tables in the database. Let the sample database is ‘geeksforgeeks’. sample tables are ‘users’ and ‘authorities’. For ‘remember me’ authentication, we need ‘persistent_logins’ as well for storing the generated tokens
use geeksforgeeks;
-- users
create table users(
username varchar(50) not null primary key,
password varchar(100) not null,
enabled boolean not null
);
-- authorities
create table authorities(
username varchar(50) not null,
authority varchar(50) not null,
constraint fk_authorities_users foreign key(username) references users(username)
);
create unique index ix_auth_username on geekAuthorities (username,authority);
create table persistent_logins(
username varchar(50) not null,
series varchar(64) primary key,
token varchar(64) not null,
last_used timestamp not null
);
Let us insert a few data into the users and authorities table for testing purposes
-- Let us create a user with admin and password as password@123
-- While storing into the database let us store as encoded password with BCryptPasswordEncoder
-- For password@123, it will be $2a$10$USD5XrNWIpf2sLnGJ62/v.hTtSIY1vdeF7v8Y4YaNJhTftbX1HBwi
insert into users(username,password,enabled)
values('admin','$2a$10$hbxecwitQQ.dDT4JOFzQAulNySFwEpaFLw38jda6Td.Y/cOiRzDFu',true);
insert into authorities(username,authority)
values('admin','ROLE_ADMIN');
To get the encoded password, by using a sample code, we can get it
Java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String password = "password@123" ;
String encodedPassword = passwordEncoder.encode(password);
|
Now let us check the database connectivity information. It is available under the src/main/resources folder
src/main/resources/database.properties
mysql.driver=com.mysql.jdbc.Driver
mysql.jdbcUrl=jdbc:mysql://localhost:3306/geeksforgeeks?serverTimezone=UTC # using geeksforgeeks
mysql.username=root # Change the appropriate username here
mysql.password=**** # Change the appropriate password here
Let us start by creating the @Configuration class and inside that define the @Bean method for DataSource
ApplicationConfiguration.java
Java
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
@Configuration
@PropertySource ( "classpath:database.properties" )
public class ApplicationConfiguration {
@Autowired
private Environment environment;
@Bean
public DataSource getDataSource() {
BasicDataSource basicDataSource = new BasicDataSource();
basicDataSource.setDriverClassName(environment.getProperty( "mysql.driver" ));
basicDataSource.setUrl(environment.getProperty( "mysql.jdbcUrl" ));
basicDataSource.setUsername(environment.getProperty( "mysql.username" ));
basicDataSource.setPassword(environment.getProperty( "mysql.password" ));
return basicDataSource;
}
}
|
Spring Security configuration
- Creation of a springSecurityFilterChain Servlet Filter is needed for protecting and validating all URLs by creating a @Configuration class.
- Need to register the springSecurityFilterChain filter with war.
- rememberMe() method of the HttpSecurity class is required to enable remember-me authentication
- Invoke the tokenRepository() method with the PersistentTokenRepository argument and store the generated tokens in the database table
Let us see how to do these
WebSecurityConfiguration.java
Java
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery( "select username, password, enabled"
+ " from users where username=?" )
.authoritiesByUsernameQuery( "select username,authority "
+ "from authorities where username=?" )
.passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().hasAnyRole( "ADMIN" , "USER" )
.and()
.authorizeRequests().antMatchers( "/login**" ).permitAll()
.and()
.formLogin().loginPage( "/login" ).loginProcessingUrl( "/loginAction" ).permitAll()
.and()
.logout().logoutSuccessUrl( "/login" ).permitAll()
.and()
.rememberMe().rememberMeParameter( "remember-me" ).tokenRepository(tokenRepository())
.and()
.csrf().disable();
}
@Bean
public PersistentTokenRepository tokenRepository() {
JdbcTokenRepositoryImpl jdbcTokenRepositoryImpl= new JdbcTokenRepositoryImpl();
jdbcTokenRepositoryImpl.setDataSource(dataSource);
return jdbcTokenRepositoryImpl;
}
}
|
SecurityWebApplicationInitializer.java
Java
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
}
|
Spring MVC Configuration is getting done by using JSP Views for the view part
WebConfiguration.java
Java
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.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@EnableWebMvc
@ComponentScan (basePackages = { "com.gfg.spring.controller" })
public class WebConfiguration implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp().prefix( "/WEB-INF/views/" ).suffix( ".jsp" );
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController( "/login" ).setViewName( "login" );
}
}
|
Servlet container Initialization and configuration can be seen by using
MvcWebApplicationInitializer.java
Java
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class MvcWebApplicationInitializer
extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { ApplicationConfiguration. class , WebSecurityConfiguration. class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebConfiguration. class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
|
Let us see the controller class now
SampleContoller.java
Java
import java.security.Principal;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class SampleContoller {
@GetMapping ( "/" )
public String index(Model model, Principal principal) {
model.addAttribute( "message" , "Welcome! You are logged by using " + principal.getName() + " username" );
return "index" ;
}
}
|
Let us see the JSP view part now
login.jsp
HTML
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html>
< html >
< head >
< meta http-equiv = "Content-Type" content = "text/html; charset=ISO-8859-1" >
< title >Spring Security</ title >
< style >
table#sample tr:nth-child(odd) {
background-color: #0074ab ;
color: yellow ;
}
table#sample tr:nth-child(even) {
background-color: #e6f7ff ;
}
table#sample {
border-collapse: collapse ;
}
table#sample td {
padding: 5px ;
}
table#sample caption {
font-style: italic ;
background-color: black ;
color: white ;
}
</ style >
</ head >
< body background = "#FFFFFF" >
< center >
< h1 >Spring Security - Remember Me Example</ h1 >
< h4 >Sample Login Form</ h4 >
< form action='<spring:url value = "/loginAction" />' method="post">
< table id = "sample" >
< tr >
< td >Username</ td >
< td >< input type = "text" name = "username" ></ td >
</ tr >
< tr >
< td >Password</ td >
< td >< input type = "password" name = "password" ></ td >
</ tr >
< tr >
< td >< input type = "checkbox" name = "remember-me" ></ td >
< td >Remember me on this Computer</ td >
</ tr >
< tr >
< td align = "center" colspan = "2" >< button type = "submit" >Login</ button ></ td >
</ tr >
</ table >
</ form >
< br />
</ center >
</ body >
</ html >
|
index.jsp
HTML
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html>
< html >
< head >
< meta http-equiv = "Content-Type" content = "text/html; charset=ISO-8859-1" >
< title >Spring Security</ title >
</ head >
< body >
< h1 >Spring Security - Remember Me Example</ h1 >
< h2 >${message}</ h2 >
< form action = "/logout" method = "post" >
< input value = "Logout" type = "submit" >
</ form >
</ body >
</ html >
|
As this is the maven project, first let us build the application from the command prompt as follows
mvn clean install
Run the application by using
mvn jetty:run
Let us test now by hitting http://localhost:8080/
admin/password@123 has to be given as credentials. As it is the user available in the user’s table and that password is kept in an encoded way. As the remember me option is selected, in the database, we can see an entry under ‘persistent_logins’
At the same time, we can check the same under cookies as well. As the chrome browser is used, let us check that via chrome browser settings options
As of now, the credentials are stored in cookies and also in DB due to the persistence mechanism, after we close the browser and open it again, we can able to see the index page when we hit http://localhost:8080. This is the advantage of remember me option. This will be possible till the expiry time. Moreover cookies data should not be cleared. Here for our example, till June 24, the cookies are not cleared, we can see the ‘Remember Me’ functionality enabled.
Whether you're preparing for your first job interview or aiming to upskill in this ever-evolving tech landscape,
GeeksforGeeks Courses are your key to success. We provide top-quality content at affordable prices, all geared towards accelerating your growth in a time-bound manner. Join the millions we've already empowered, and we're here to do the same for you. Don't miss out -
check it out now!
Last Updated :
03 Nov, 2022
Like Article
Save Article