Works by

Ren's blog

プログラミング等IT技術関連でメモする

【Spring】Spring Sessionでセッション情報をDb2上に保存する

f:id:rennnosukesann:20181220183020p:plain

Spring でのセッション

SpringではJava ServletのHttpSessionの仕組みを利用することができます。ServletのHttpSessionはSpringフレームワークの上に構築されたアプリケーション上でそのまま利用できるほか、Springの各種ライブラリによってラッピングされた形でも利用することができます。

アプリケーションサーバを複数台構成とした場合のセッション

特に設定を行わなかった場合、Servletのセッションはメモリ上で管理されます。もし複数台のサーバや複数のクラウドインスタンスにアプリケーションがデプロイされていると、各インスタンス間で個別のメモリ領域を持つことになり、互いにセッションの情報が共有されません。そのため、例えばあるインスタンス上アプリケーションでログインに成功したユーザが、別のインスタンス上アプリにアクセスすると未ログイン状態として扱われてしまう・・・といったことが起こります。

このような問題に対する解決策はいくつかあるのですが、今回はDB上にセッション情報を保存する方法を紹介します。

適用方法

今回はDBMSとしてDb2を使用します。 またSpringアプリケーションのビルドツールとしてGradleを利用し、 すでにDBMSへの接続は完了しているものとします。

Spring Sessionの導入

セッションの管理をDB上で行えるようにします。 build.gradle に下記パッケージを追加します。

dependencies {
    ....
    implementation('org.springframework.boot:spring-boot-starter-web')
    implementation('org.springframework.boot:spring-boot-starter-data-jpa')
    implementation("org.springframework.session:spring-session-core")
    implementation("org.springframework.session:spring-session-jdbc")
    ....
}

Configuration Beanクラスの定義

@EnableJdbcHttpSession アノテーションを付加した Config クラスを定義します。これにより、JDBC経由でDB上でセッションを管理できるようになります。

import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.session.jdbc.config.annotation.web.http.EnableJdbcHttpSession;
import org.springframework.transaction.PlatformTransactionManager;

/**
 * 
 * <p>
 * セッション情報をJDBC経由でDB上で管理するためのConfiguration
 * </p>
 * 
 */
@EnableJdbcHttpSession
public class JdbcHttpSessionConfig {
  @Bean
  public PlatformTransactionManager transactionManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
  }
}

Spring設定ファイルの変更

application.properties に下記項目を追加します。

# Session Setting in DB
server.session.jdbc.initialize-schema=always
server.session.jdbc.table-name=SPRING_SESSION

application.yml の場合は以下。

server:
    sesion:
        jdbc:
            initialize-schema: always
            table-name: SPRING_SESSION

2019/06/02追記

application.yml application.properties なしでも動作します。 テーブル名をデフォルトの SPRING_SESSION から変更したい場合 @EnableJdbcHttpSession のプロパティ tableName に値を渡します。

@EnableJdbcHttpSession(tableName = "APP_SESSION")

テーブルの追加

設定は上記で完了なのですが、このままではセッションを保存できません。 既存のDBにセッション情報を追加するテーブルを定義する必要があります。

Spring SessionのGitHubリポジトリDBMS別のテーブルCREATE用SQLがおいてあるので、こちらを活用しましょう。

ex. Db2の場合(少し編集してあります):

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)
)
ORGANIZE BY ROW;

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);

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
)
ORGANIZE BY ROW;

Db2は列指向*1を定義する機能を持ち、一部データ分析用DBではこの機能がデフォルトになっていたりします(IBM Cloud Db2 Warehouseなど)。この方針のままだと、BLOB 型定義をサポートしていない仕様のため*2、上記SQLでは元のSQLに明示的に ORGANIZE BY ROW を指定し、メジャーなRDBMS同様の行指向操作がされるようにしています。


2019/06/02追記

@EnableJdbcHttpSession にてテーブル名を変更した場合、上記SQLでもテーブル名を変更します。


これで、DB上にセッションが保持されるはずです!

f:id:rennnosukesann:20190204174125p:plain

参考文献

qiita.com

qiita.com

*3 ja.wikipedia.org

*1:列指向の概念をよくわかっていない...
曰く、通常のRDBMSで行単位での操作をするように、列単位での操作を主とするらしい

*2:https://www-01.ibm.com/support/docview.wss?uid=swg22003176

*3:完全にDBMS=RDBMSの文脈前提で語っている...