-->
  • Manage User Session With Spring JDBC Session




    This 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 it 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 WebSecurityJPAH2, and Session starters. I have used the Spring Initializer to generate the project.
    Image title


    By default, the Spring starter will add the  org.springframework.session:spring-session-core dependency. Let us change it to  spring-session-jdbcas 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=jdbcHere, 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 the 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!

  • You might also like

    No comments:

    Post a Comment

Contact Form

Name

Email *

Message *