Spring Session Data Redis でセッションリプリケーション方法
Tech > Java > SpringFramework
Spring Session Data Redis を利用したセッションのリプリケーション
AWS上のTomcatでSpringFrameworkを動作させた環境でRedisを利用したセッションの共有化を行う方法の備忘録。
AWS上でTomcatを利用する注意点としてマルチキャストが使えないため、Tomcatがもつネイティブなセッションクラスタリングが利用できないため、ログイン情報などセッション関連の情報がTomcatの再起動でお亡くなりなるケースだとメンテナンスで辛い。
その辺の回避策として、なるべくサービス継続できるようセッションの状態を維持するようにする。
インフラ構成
フロントへリバースプロキシを置き、2台のtomcatでセッションを共有する、認証情報はデータベースへ保存しログインを実行する
変更箇所
まず、前提としてSpring Session Data Redis を利用したセッションのリプリケーションSpringFramework5が必須らしい。
pom.xml
指定されているバージョンについては適当
Redis client driver lettuce
クライアントドライバーにlettuceを利用
<!-- https://mvnrepository.com/artifact/biz.paluch.redis/lettuce -->
<dependency>
<groupId>biz.paluch.redis</groupId>
<artifactId>lettuce</artifactId>
<version>4.5.0.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.lettuce/lettuce-core -->
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>6.0.2.RELEASE</version>
</dependency>
spring-session-data-redis
Redis用のセッションモジュール
<!-- https://mvnrepository.com/artifact/org.springframework.session/spring-session-data-redis -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>2.4.2</version>
</dependency>
web.xml
クッキーまわりの設定はRedis関連の設定から行うので必要最低限の設定
session-configセクション
SESSIONIDはクッキーから取得するよう変更
<session-config>
<tracking-mode>COOKIE</tracking-mode>
</session-config>
filterセクション
TomcatのHttpSessionをSpringFrameworkのものでfilterにより差し替える設定を行う、 厳密にいうと HttpSession を利用するモジュールより前に書く必要があるらしいので、SpringSecurity 関連のものの前に記載しておく
<filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy ...
context-paramセクション
外部のxml設定ファイルを読み込んでいる場合、session-context.xmlの記載を追加する (どこに書いてもいいと思うので、必要であれば実施)
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:WEB-INF/spring/security-context.xml
classpath:WEB-INF/spring/mybatis-context.xml
classpath:WEB-INF/spring/session-context.xml</param-value>
</context-param>
session-context.xml
セッションの設定を行う
- maxInactiveIntervalInSeconds
- セッションのタイムアウトを秒で指定
- RedisStandaloneConfiguration
- この設定は単一のRedisインスタンス向け接続設定であり、クラスタリングに非対応
- ConfigureRedisAction.NO_OP
- Amazon ElastiCacheなどで接続エラーが発生するようなのですが未確認
IPやPORTは適切修正
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd">
<context:annotation-config/>
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
<property name="maxInactiveIntervalInSeconds" value="1800"/>
</bean>
<bean id="redisConfig" class="org.springframework.data.redis.connection.RedisStandaloneConfiguration">
<property name="hostName" value="192.168.0.53" />
<property name="port" value="6379"/>
<property name="database" value="1" />
</bean>
<bean class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory">
<constructor-arg index="0" ref="redisConfig" />
</bean>
<!-- <util:constant static-field="org.springframework.session.data.redis.config.ConfigureRedisAction.NO_OP"/> -->
</beans>
security-context.xml
CSRFタグの取得をクッキーから行うように、TokenRepositoryを明示的に指定
<sec:csrf token-repository-ref="csrfTokenRepository" />
refを以下のBeanで設定
<bean id="csrfTokenRepository" class="org.springframework.security.web.csrf.CookieCsrfTokenRepository" />
認証
認証については、AuthenticationManagerを利用した普通のフォーム認証のため、面倒くさいので割愛
Controllerの実装方法
SessionAttributeを利用した実装方法
クラス定義
typeを利用した記載方法、valueの利用方法も一般的なSpringFrameworkのやり方と同様
@Controller
@SessionAttributes(types = { SessParam.class })
public class PutSessionController {
...
セッションから取得
コントローラ内のハンドラ関数の引数で@ModelAttributeと名前でDIさせる
...@RequestMapping(value = "/putsess", method = RequestMethod.GET)
public String showSess(@ModelAttribute("sessParam") SessParam sessParam, Model model) {
info("putss: get count = " + sessParam.getCount());
logger.addAttribute("sessParam", sessParam);
model.return "putsess";
} ...
セッションへ保存
セッションへ保存するインスタンスのsetterメソッドなどで情報を変更する、セッションストレージへの保存タイミングはレスポンスを返送したときがディフォルト(うる覚え)
...@RequestMapping(value = "/putsess", method = RequestMethod.POST)
public String putSess(@ModelAttribute("sessParam") SessParam sessParam) {
if(sessParam.getCount() == null) {
setCount(1);
sessParam.else {
} setCount(sessParam.getCount()+1);
sessParam.
}info("putss: inc count = " + sessParam.getCount());
logger.return "redirect:/putsess";
} ...
セッションのリセット
sessionStatus.setComplete(); を利用してリセット
...@RequestMapping(value = "/putsess/reset", method = RequestMethod.POST)
public String putSessReset(SessionStatus sessionStatus) {
setComplete();
sessionStatus.return "redirect:/putsess";
} ...
動作時の注意点
tomcatの再起動とインスタンス復元時の注意点
Tomcatの再起動によってRedisとの接続が切れて再接続した場合、以下の書き方だと新しいオブジェクトのインスタンスが返ってくるので実際の動作で許容できない場合書き換える必要がある。
... Controller内のオブジェクト
...@ModelAttribute("sessParam")
public SessParam setupSessParam() {
return new SessParam();
} ...
これを回避したい場合、以下のように修正する
@Controller
@SessionAttributes(types = { SessParam.class })
public class PutSessionController {
@Autowired
protected HttpSession session;
...@ModelAttribute("sessParam")
public SessParam setupSessParam(HttpServletRequest request) {
getAttribute("sessParam");
SessParam obj = (SessParam)session.if(obj == null) {
return new SessParam();
else {
} return obj;
}
} ...
HttpSessionインスタンスをAutowiredして、そこからオブジェクトをRedis内に確認し無い場合だけ新しいインスタンスを生成して返す、こうすることによって、tomcatの再起動後も同じインスタンスを再生成できるようになる
nginxのproxy_cookie_pathの注意点
sptest3はアプリケーションコンテキストとして以下のような書き方だとクッキーが正常に取得できず、ログイン認証が動かなかった
tomcatのHttpSession版 nginx設定の場合、以下のような設定で動いていたのが、Spring Session Data Redisに切り替えた際、Redisにデータがストアさせるが、ログイン後の認証情報が取得できないという事象に遭遇した
proxy_cookie_path /sptest3 /;
原因は、レスポンスヘッダのCookie Pathが Path=//; のように出力されていたためで
proxy_cookie_path ~*^/.* /; # --> proxy_cookie_path /sptest3/ /; 後に確認したところ、この書き方もNGでした
のようにスラッシュを追加し、Path=/; のように出力することで解決した、この設定については、nginxの他のProxy設定が違っていた可能性もあるので正確性にはかけていると思う
Posted on 2021-03-20 06:36:09