SpringBootでSessionをDB管理で永続化する方法


## 概要
SpringSessionをそのまま使用すると、デフォルトであるとサービスのメモリ内でセッション情報を管理しているため、複数サーバでロードバランスしている場合などセッション情報が複数台サーバにある場合、使い勝手が悪いという弱点があります。

上記弱点を補うため、セッション情報を外部で管理する方法があり、管理方法はいくつかありますが、今回はDB（Postgresq1）に保存する方法を例に紹介しようとおもいます。



## SpringSession 設定
SpringSessionがあらかじめ反映されている状態からDB設定を実施する手順を示します。

### 依存関係の追加
SpringSessionでDB格納する依存関係を追加する
``` build.gradle
dependencies {
    implementation 'org.springframework.session:spring-session-jdbc'
}
```

### SpringSession保存方式を指定
SpringSessionでSession情報をDB保存するよう「spring.session.store-type」に「jdbc」を指定

``` application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
spring.datasource.username=testDB
spring.datasource.password=xxxx

spring.session.store-type=jdbc
```

## DDL設定

Session情報を格納するDDLをDBに反映する
``` session.ddl
CREATE TABLE public.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 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 BYTEA 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	
);
```
参照元
https://github.com/spring-projects/spring-session/tree/main/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc

## Java実装

Controllerクラスで、HttpSessionに対しSession情報を設定

``` ApiController.java

public class TestApiController {
	 
	@GetMapping
	public String getTest(HttpSession session) {
		
		session.setAttribute("sample", "sample");

		return "retValue";
	}
```

## DB格納状態の確認
上記を実行しSession情報が格納されているかについて確認する

### 「SPRING_SESSION」テーブルを確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172093/59ec1f1c-0c1f-01cb-32b5-8ce17b4b1b85.png)
Session登録時刻、最終更新時刻、Sessionタイムアウトまでの時刻、タイムアウトまでの時間が設定される

### 「SPRING_SESSION_ATTRIBUTES」テーブルを確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172093/6b0780ae-f2d2-fd2b-d124-7a0120db027b.png)
HttpSettionで設定されたキーと値が設定される、値はバイナリ値で格納される


## ブラウザ側のSession情報を確認
ブラウザ側にも同一セッション情報で渡ってきていることを確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172093/036de9d7-1787-2bdf-9ef1-fe5dc1c31f35.png)
SessionIDはBase64エンコードされているため、デコードし確認する
「c8a2ba96-75c1-4ac1-9bf7-268d821ca05e」となり、DBに保存されているセッションコードと一致していることを確認できる

## その他カスタマイズ

### 再起動時テーブル初期化指定
サービス起動時にDB保存したSession情報の取り扱いについて
「spring.session.jdbc.initialize-schema」で指定できる
常に初期化する：ALWAYS
Embedded Databaseのみ初期化する：EMBEDDED
なにもしない：NEVER

```
spring.session.jdbc.initialize-schema=NEVER
```

### テーブル名カスタマイズ指定
「spring.session.jdbc.table-name」にカスタマイズしたテーブル名を記載
テーブル構成はデフォルト指定のテーブルと同一にする必要あり
```
spring.session.jdbc.table-name=SPRING_SESSION
```

### クリーンアップタイミング指定
期限切れのセッションによるデータベースのオーバーロードを避けるために、Spring Session JDBC は期限切れのセッション (およびその属性) を削除するクリーンアップジョブを 1 分ごとに実行します。

下記アノテーションを設定することで、クリーンアップ間隔を指定できます
```
@EnableJdbcHttpSession(cleanupCron = "0 0 * * * *") // top of every hour of every da
```

# 参考資料

https://spring.pleiades.io/spring-session/reference/configuration/jdbc.html

https://spring.pleiades.io/spring-session/reference/guides/boot-jdbc.html

https://qiita.com/nekopanchi0423/items/c6051c4dafcd6e0bc881

https://qiita.com/pipi0813/items/93e60c605f89f93e49fb#chrome

### タイムスタンプ変換確認
https://url-c.com/tc/

### Base64変換確認
https://www.en-pc.jp/tech/base64/#result