Tomcatセッションを外部ストレージに置き換える場合にどうしてもTomcat側で難しい設定をしたり、ライブラリを配置したりで億劫になることが多かった。
pom.xmlにdependencyを追加したら置き換わるくらいの簡単なもので、且つセッションストレージがRedisとなるものを探していた。
そんなところにSpringのリリースブログで今月初めにこんな記事を見つけた。
Spring Session 1.0.0.RELEASE
TODO (2015/04/14追記)
あとでまとめる
- CookieHttpSessionStrategyにバグがあるため
@EnableRedisHttpSession#maxInactiveIntervalInSeconds
の値が反映されない → https://github.com/spring-projects/spring-session/issues/106 - SessionAttributeで保存したデータ構造が変わった時の対応方法の記述
Spring-Sessionを使ってみての結論
先に結論言っちゃいます。
以下、問題なければ使えます。
Sessionの張り替え
できません。
session.invalidate();
session.getSession(true);
って同じリクエスト内でやっても生成されたJSESSIONIDはCookieに設定されなかった。
破棄されたJSESSIONIDが次のリクエストで設定されてたからセッションエラーになる。
SessionAttributeの書き換え
User user = (User) session.getAttribute("sessionUser");
System.out.println(user.getName()); // John
user.setName("Emily");
User user = (User) session.getAttribute("sessionUser");
System.out.println(user.getName()); // John
TomcatデフォルトのHttpSession実装はEmily
になる。
Spring Sessionとは
こんなことができるらしい。
- API and implementations (i.e. Redis) for managing a user’s session
- HttpSession - allows replacing the HttpSession in an application container (i.e. Tomcat) neutral way. Additional features include:
- Clustered Sessions - Spring Session makes it trivial to support clustered sessions without being tied to an application container specific solution.
- Multiple Browser Sessions - Spring Session supports managing multiple users’ sessions in a single browser instance (i.e. multiple authenticated accounts similar to Google).
- RESTful APIs - Spring Session allows providing session ids in headers to work with RESTful APIs
- WebSocket - provides the ability to keep the HttpSession alive when receiving WebSocket messages
日本語訳だとこうなる。(適当に翻訳したので悪しからず。内容は間違ってないはず。)
- ユーザーセッションを管理するAPIとその実装(例えばRedis)を提供する。
- HttpSession - アプリケーションコンテナ(例えばTomcat)のHttpSessionに仲介する形でその機能を置き換える。さらに追加する機能は次の通り。
- クラスターセッション - アプリケーションコンテナ固有の問題に縛られることなく、クラスタリングされたセッションをサポートすることができる。
- 複数セッション - 1つのブラウザで複数のユーザーセッションを管理することができる。(例えばGoogleのような複数の認証アカウントが存在する場合がこれにあたる。)
- RESTful API - RESTfulなAPIでも動作するようにセッションIDをHTTPヘッダで提供できる
- WebSocket - WebSocketでメッセージを受け取ったときもHttpSessionを有効にし続ける機能を提供する
らしい。1つめと2つめの内容で試してみたいと思った。
既存のWebアプリケーションに導入
Springを利用している既存のWebアプリケーションに入れてみた。
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>1.0.0.RELEASE</version>
</dependency>
<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>
</filter-mapping>
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 180) // 3分
public class RedisHttpSessionConfig {
// XMLで定義済みで他目的のRedisConnectionFactoryと
// 接続先を分けるときの対応として名前付きでInjectする
@Resource(name = "session.redisConnectionFactory")
private RedisConnectionFactory redisConnectionFactory;
@Bean
public RedisConnectionFactory connectionFactory() {
return redisConnectionFactory;
}
// セッションIDをCookieで管理する設定
@Bean
public CookieHttpSessionStrategy httpSessionStrategy() {
CookieHttpSessionStrategy httpSessionStrategy = new CookieHttpSessionStrategy();
httpSessionStrategy.setCookieName("JSESSIONID");
return httpSessionStrategy;
}
}
RedisHttpSessionConfig.java
は自前で用意。コンポーネントスキャン対象のパッケージ以下に配置しておく。
Springの設定ファイルをXMLで管理していたけど@EnableRedisHttpSession
が色々やってくれるからここだけは大人しくJavaConfigにしておく。
余談だけどこういう書き方もできる。
// @Resource(name = "session.redisConnectionFactory")
// private RedisConnectionFactory redisConnectionFactory;
@Bean
public RedisConnectionFactory connectionFactory(@Qualifier("session.redisConnectionFactory") RedisConnectionFactory redisConnectionFactory) {
return redisConnectionFactory;
}
設定終わり。これだけでSession管理がアプリケーションコンテナからSpring-SessionのRedis実装に置き換わった。