Manage User Session With Spring JDBC Session
Want to learn more about how to manage user sessions with the Spring JDBC Session? Check out this post to learn how to enable Spring Session on your app.
Join the DZone community and get the full member experience.
Join For FreeThis article will demonstrate how to configure and use the Spring Session to manage session data in a web application with Spring Boot. For a more in-depth look at the code, check out this GitHub repository.
Introduction
In a web application, user session management is crucial for managing user state. Spring Session is an implementation of four approaches, storing session data in a persistent data store. Spring Session supports multiple datastores, like RDBMS, Redis, HazelCast, MongoDB, etc., to save the user session data.
Spring Session Benefits
- Spring Session decouples the session management logic from the application, making it more tolerant.
- Spring Session keeps information in the database, so it’s great to use in a clustered environment with multiple server nodes. Because of this, we don’t need to rely on the sticky session or session replication logic.
- As session data is stored in the database, user session data is not lost if the application crashes. When the application started again, it picks up the user session from the database.
- It is easy to switch between session storage. Just by changing the configuration, we can switch from using JDBC to Redis.
Create Your Spring Boot Application
Let’s start by creating a simple Spring Session JDBC example, using the latest version, with Web, Security, JPA, H2, and Session starters. I have used the Spring Initilizer to generate the project.
By default, the Spring starter will add the org.springframework.session:spring-session-core
dependency. Let us change it to spring-session-jdbc
, as we are going to use JDBC backend.
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-jdbc</artifactId>
</dependency>
Configure Spring JDBC Session Properties
The application.properties
file specifies the H2 database configuration and Spring Session attributes.
# Session store type.
spring.session.store-type=jdbc
# Database schema initialization mode.
spring.session.jdbc.initialize-schema=embedded
# Path to the SQL file to use to initialize the database schema.
spring.session.jdbc.schema=classpath:org/springframework/session/jdbc/schema-@@platform@@.sql
# Name of the database table used to store sessions.
spring.session.jdbc.table-name=SPRING_SESSION
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:testdb;INIT=RUNSCRIPT FROM 'classpath:/schema-db2.sql'\\;
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto = create
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
We added the property spring.session.store-type=jdbc
. Here, we specify using JDBC to store the session data.
As we are using the H2 in-memory database, Spring Session creates the following tables required to store the session data automatically from the script:
CREATE SCHEMA IF NOT EXISTS TESTDB;
SET SCHEMA TESTDB;
CREATE TABLE TESTDB.SPRING_SESSION (
PRIMARY_ID CHAR(36) NOT NULL,
SESSION_ID CHAR(36) NOT NULL,
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
EXPIRY_TIME BIGINT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
);
CREATE UNIQUE INDEX SPRING_SESSION_IX1 ON TESTDB.SPRING_SESSION (SESSION_ID);
CREATE INDEX SPRING_SESSION_IX2 ON TESTDB.SPRING_SESSION (EXPIRY_TIME);
CREATE INDEX SPRING_SESSION_IX3 ON TESTDB.SPRING_SESSION (PRINCIPAL_NAME);
CREATE TABLE TESTDB.SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,
ATTRIBUTE_NAME VARCHAR(200) NOT NULL,
ATTRIBUTE_BYTES BLOB NOT NULL,
CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME),
CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES TESTDB.SPRING_SESSION(PRIMARY_ID) ON DELETE CASCADE
);
But, if we are going to another RDBMS database, such as My SQL, then we need to add the My SQL dependency.
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
And add my sql database configuration in application.properties file
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/testdb
spring.datasource.username=root
spring.datasource.password=admin
Enable the Spring Session table creation using the spring.session.jdbc.initialize-schema
property.
With this property, Spring will try to create the session tables using the script classpath:org/springframework/session/jdbc/schema-@@platform@@.sql.
So, in this case, it will use schema-mysql.sql
spring.session.jdbc.initialize-schema=always
If we specify spring.session.jdbc.initialize-schema=never
, then we need to create session tables in manually executing the script. In production, we don't enable the auto-create/update.
Spring Session With Spring Security
We are using Spring Security for user authentication. Therefore, we are integrating Spring Session with Spring Security.
Let's add the Spring Security to our application:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Now, we create the SpringSecurityConfig
class to enable the Spring Security.
package com.manoj.training.spring.springsessiondemo;
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.WebSecurity;
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;
@EnableWebSecurity
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user1").password(passwordEncoder().encode("user1")).roles("USER").and()
.withUser("admin").password(passwordEncoder().encode("admin")).roles("admin");
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/h2-console/**");
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
@EnableWebSecurity
enables the Spring Security in our application. Here, we have configured two users, User1 and Admin, in the configure method.
Create the RestController
Here, we will create a HelloController
class. We are going to expose the hello REST endpoint, it will return "Hello World."
package com.manoj.training.spring.springsessiondemo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String sayHello(){
return "Hello World";
}
}
SpringBootApplication
class:
package com.manoj.training.spring.springsessiondemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.session.jdbc.config.annotation.web.http.EnableJdbcHttpSession;
@SpringBootApplication
@EnableJdbcHttpSession
public class SpringSessionDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringSessionDemoApplication.class, args);
}
}
The @EnableJdbcHttpSession
annotation creates a Spring Bean with the name of springSessionRepositoryFilter
that implements the filter. The filter is what is in charge of replacing the HttpSession
implementation to be backed by Spring Session. In this instance, Spring Session is backed by a relational database. By default, the session timeout is 1800 seconds (30 minutes).
Now, if we run our Spring Boot application and access http://localhost:8080/hello, it should redirect to an auto-generated login page. Now, login with the configured users and see the data in the SPRING_SESSION
table. You can see that the login username is stored in the PRINCIPAL_NAME
column.
Below is a video that shows the working demo of the application.
Summary
In this article, we learned how we can manage user session effectively by using Spring Session with very minimal configuration with the Spring Boot auto-configuration. We can easily change the backend from JDBC to Redis or HazeCast just by simply changing the configuration.
If you have anything that you want to add or share, then please share it in the comment section below.
Happy Learning!
Opinions expressed by DZone contributors are their own.
Comments