Spring Security 5 - Remember Me authentication example with Hibernate 5

Posted on January 18, 2018


This post shows you how to implement the persistent token based remember-me service in Spring MVC application with Hibernate framework.

In this example, we will discuss only how to store and retrieve the persistent login tokens from a database using Hibernate. 

Please read our previous post to learn – 

  • How to integrate spring security with Hibernate framework?
  • How to implement UserDetailsService to load user's authentication details from a database using Hibernate?

Continuing with our previous example, let’s see how to persist and retrieve the tokens with Hibernate.

Project structure

Final project structure of our application will look like as follows.

spring-security-hibernate-rem.png

Note – No changes are needed in db.properties, MvcWebApplicationInitializer.java, SecurityWebApplicationInitializer.java, WebConfig.java, UserDetailsDaoImp.java, UserDetailsServiceImp.java, MyContoller.java and index.jsp files. Please refer our previous post for these files.

Database table creation

Use the following DDL statements to create a table for persistent logins in MySQL database.

create table logins_persistent(
	username varchar(50) not null,
	series varchar(64) primary key,
	token varchar(64) not null,
	last_used timestamp not null
);

Entity class

Create an @Entity class, named as PersistentLogins, to map with logins_persistent table as follows.

PersistentLogins.java

package com.boraji.tutorial.spring.model;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "LOGINS_PERSISTENT")
public class PersistentLogins {

  @Id
  @Column(name = "SERIES")
  private String series;

  @Column(name = "USERNAME", nullable = false)
  private String username;

  @Column(name = "TOKEN", nullable = false)
  private String token;

  @Column(name = "LAST_USED", nullable = false)
  private Date lastUsed;

  //Setter and Getter methods
}

Hibernate configuration

Register the PersistentLogins.java entity with Hibernate SessionFactory  as follows. 

public class AppConfig {

  @Bean
  public LocalSessionFactoryBean getSessionFactory() {
    LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();

    Properties props = new Properties();

    // Setting JDBC properties
    //...

    // Setting Hibernate properties
    //..

    // Setting C3P0 properties
    //...
    //...
    factoryBean.setHibernateProperties(props);
    factoryBean.setAnnotatedClasses(User.class, Authorities.class, PersistentLogins.class);

    return factoryBean;
  }

}

Repository class

To implement the persistent token based remember me service, you can create a PersistentTokenDaoImp repository class by implementing the PersistentTokenRepository interface as follows.

PersistentTokenDaoImp.java

package com.boraji.tutorial.spring.dao;

import java.util.Date;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.boraji.tutorial.spring.model.PersistentLogins;

@Repository("persistentTokenRepository")
@Transactional
public class PersistentTokenDaoImp implements PersistentTokenRepository {

  @Autowired
  private SessionFactory sessionFactory;

  @Override
  public void createNewToken(PersistentRememberMeToken token) {
    PersistentLogins logins = new PersistentLogins();
    logins.setUsername(token.getUsername());
    logins.setSeries(token.getSeries());
    logins.setToken(token.getTokenValue());
    logins.setLastUsed(token.getDate());
    sessionFactory.getCurrentSession().save(logins);
  }

  @Override
  public PersistentRememberMeToken getTokenForSeries(String seriesId) {
    PersistentLogins logins = sessionFactory.getCurrentSession()
          .get(PersistentLogins.class, seriesId);
    
    if (logins != null) {
      return new PersistentRememberMeToken(logins.getUsername(), 
          logins.getSeries(), logins.getToken(),logins.getLastUsed());
    }

    return null;
  }

  @Override
  public void removeUserTokens(String username) {
    sessionFactory.getCurrentSession().createQuery("delete from PersistentLogins" 
        + " where username=:userName")
        .setParameter("userName", username).executeUpdate();
  }

  @Override
  public void updateToken(String series, String tokenValue, Date lastUsed) {
   Session session=sessionFactory.getCurrentSession();
    PersistentLogins logins=session.get(PersistentLogins.class, series);
    logins.setToken(tokenValue);
    logins.setLastUsed(lastUsed);
  }

}

 

Spring Security configuration

Inject the UserDetailsService and PersistentTokenRepository in your Spring security @Configuration class and configure the remember-me authentication as follows.

WebSecurityConfig.java

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

  @Autowired
  private UserDetailsService userDetailsService;
  
  
  @Autowired
  @Qualifier("persistentTokenRepository")
  private PersistentTokenRepository persistentTokenRepository;
  
  @Bean
  public BCryptPasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  };
  
  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
  }

  @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(persistentTokenRepository).userDetailsService(userDetailsService)
    .and()
    .csrf().disable();
  }
}

The UserDetailsService class is used to look up user details when remember-me token is valid.

JSP View

Add new <input type="checkbox"> tag in your custom login form for remember-me option as follows.

login.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
	pageEncoding="ISO-8859-1"%>
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>BORAJI.COM</title>
</head>
<body>

	<h1>Spring MVC 5 + Spring Security 5 + Hibernate 5 example</h1>
	<h4>Login Form</h4>
	
	<form action='<spring:url value="/loginAction"/>' method="post">
    <table>
      <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><button type="submit">Login</button></td>
      </tr>
    </table>
  </form>
  <br/>
</body>
</html>

Run application

Use the following maven command to run your application.

mvn jetty:run

Enter the http://localhost:8080 URL in browser's address bar to test our application.

On entering the URL, you will see the login page asking for username and password with remember-me option as follows.

spring-security-hibernate-rem_01.png

On successful login, you will see the index page as follows.

spring-security-hibernate-rem_02.png

Verify remember-me tokens in database table -

spring-security-hibernate-rem_03.png