daisuzz.log

Spring BootアプリケーションのセッションをDBで管理する

はじめに

今回は、Spring BootアプリケーションのセッションをDBで管理する方法を調べたので備忘録として書いていきます。

書いてある内容は手を動かして確認したものですが、正しい情報は公式ドキュメント(以下は2.4.3のもの)の内容を見てください。 Spring Session - Spring Boot

環境

  • Spring Boot 2.4.5
  • Maven 3.6.3
  • Kotlin 1.3.72-release-468
  • Docker version 20.10.8, build 3967b7d
  • MySQL 5.7

spring-session-jdbcの依存を追加

Spring Session JDBC を利用するため、pom.xmlにspring-session-jdbcを追加します。 Spring Session JDBCは、RDBを使ったSessionRepositoryの実装クラスと、関連する設定を提供するライブラリです。

        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-jdbc</artifactId>
        </dependency>

セッション関連の設定を追加

application.properties(application.yml)にセッションに関連する設定を追加していきます。

spring.session.store-type=jdbc
spring.session.jdbc.initialize-schema=embedded
spring.session.jdbc.table-name=SPRING_SESSION
server.servlet.session.timeout=30m

spring.session.jdbc.initialize-schemaは、スキーマの初期化をどういう条件で実行するのかを設定するプロパティです。ALWAYSであれば常にDBの初期化が行われ、EMBEDDEDであればembedded databaseに対してのみ初期化が行われ、NEVERであればデータベースの初期化を行わない、という挙動になります。 spring.session.jdbc.table-nameはセッションを格納するテーブル名を設定するプロパティです。ここで設定されたテーブルにセッションを格納します。

データベースの接続情報の設定を追加

application.properties(application.yml)にデータベースの接続情報の設定を追加していきます。

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/sample
spring.datasource.username=user
spring.datasource.password=password

セッションを格納するテーブルを作成する

デフォルトでは、SPRING_SESSION, SPRING_SESSION_ATTRIBUTESテーブルにセッションを格納するため、 以下のリポジトリで管理されているスキーマごとのSQLを実行してテーブルをデータベースに作成します。 https://github.com/spring-projects/spring-session/tree/main/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc

今回は、SpringBootアプリケーションコンテナとは別に、DockerでMySQLコンテナを立ち上げるので、その初期化SQLに↑で管理されているMySQL用のスキーマを記述します。

DROP TABLE IF EXISTS SPRING_SESSION;
CREATE TABLE 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)
) ENGINE = InnoDB
  ROW_FORMAT = DYNAMIC;

CREATE UNIQUE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE INDEX SPRING_SESSION_IX3 ON SPRING_SESSION (PRINCIPAL_NAME);

DROP TABLE IF EXISTS SPRING_SESSION_ATTRIBUTES;
CREATE TABLE 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 SPRING_SESSION (PRIMARY_ID) ON DELETE CASCADE
) ENGINE = InnoDB
  ROW_FORMAT = DYNAMIC;

セッションとして格納するクラスをSerializableにする

今回はSpring Securityで提供されているログイン機能を使うため、UserDetailsクラスを実装したクラスをセッション情報として格納します。 セッション情報として格納するクラスはSerializableなクラスとして実装する必要があるため、Serializableインターフェースを実装するよう変更します。

data class TodoUser(
    val userId: String,
    val userName: String,
    val password: String
) : Serializable

動作確認

実際にサンプルアプリケーションを立ち上げてログインしてみると、SPRING_SESSION, SPRING_SESSION_ATTRIBUTESテーブルにレコードが作成されていることが確認できます。

参考資料

https://docs.spring.io/spring-session/docs/2.4.3/reference/html5/guides/boot-jdbc.html