Spring-Sessionを使ってみた

Tomcatセッションを外部ストレージに置き換える場合にどうしてもTomcat側で難しい設定をしたり、ライブラリを配置したりで億劫になることが多かった。

pom.xmlにdependencyを追加したら置き換わるくらいの簡単なもので、且つセッションストレージがRedisとなるものを探していた。

そんなところにSpringのリリースブログで今月初めにこんな記事を見つけた。
[Spring Session 1.0.0.RELEASE](http://spring.io/blog/2015/01/08/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の張り替え

できません。

```java
session.invalidate();
session.getSession(true);
```

って同じリクエスト内でやっても生成されたJSESSIONIDはCookieに設定されなかった。
破棄されたJSESSIONIDが次のリクエストで設定されてたからセッションエラーになる。

### SessionAttributeの書き換え

```java:request1
User user = (User) session.getAttribute("sessionUser");
System.out.println(user.getName()); // John
user.setName("Emily");
```

```java:request2
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を有効にし続ける機能を提供する

らしい。１つめと２つめの内容で試してみたいと思った。

## 既存のWebアプリケーションに導入

Springを利用している既存のWebアプリケーションに入れてみた。

```xml:pom.xml
<dependency>
  <groupId>org.springframework.session</groupId>
  <artifactId>spring-session-data-redis</artifactId>
  <version>1.0.0.RELEASE</version>
</dependency>
```

```xml:web.xml
<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>
```

```java:RedisConfig.java
@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にしておく。

余談だけどこういう書き方もできる。

```java
  // @Resource(name = "session.redisConnectionFactory")
  // private RedisConnectionFactory redisConnectionFactory;

  @Bean
  public RedisConnectionFactory connectionFactory(@Qualifier("session.redisConnectionFactory") RedisConnectionFactory redisConnectionFactory) {
    return redisConnectionFactory;
  }
```

設定終わり。これだけでSession管理がアプリケーションコンテナからSpring-SessionのRedis実装に置き換わった。