【Springセキュリティ】セッション管理の基本とタイムアウト設定
新人
「先輩、Webアプリのセッションってよく聞くんですけど、実際には何を管理してるんですか?」
先輩
「セッションは、ユーザーがログインしてからログアウトするまでの一連のやり取りを管理する仕組みだよ。ログイン状態やカートの中身などを保持するために使われるんだ。」
新人
「なるほど、状態を記憶してるんですね。でも、それってどうやって保持されるんですか?」
先輩
「それが『セッションID』という仕組みで、ブラウザとサーバー間で識別して管理してるんだ。じゃあ、Spring Securityのセッション管理についても見てみようか。」
1. セッションとは?Webアプリにおけるセッションの役割
Webアプリ セッションとは、ユーザーがログインしてからログアウトするまで、あるいはタイムアウトするまでの間、状態を維持するための仕組みです。
HTTPは本来「ステートレス(状態を保持しない)」な通信手段ですが、ログイン状態の維持や、買い物カゴの情報の保持といった機能を実現するために、セッション管理が重要になります。
具体的には、サーバー側で「セッションオブジェクト」を生成し、クライアントにはその識別子(JSESSIONID)がクッキーなどを通じて渡され、リクエストのたびに同じユーザーであることを識別します。
例えば以下のようなイメージです。
<!-- ログイン後にサーバーがセッションIDを発行 -->
Set-Cookie: JSESSIONID=ABCD1234; Path=/; HttpOnly
このJSESSIONIDがユーザーを一意に識別する鍵になり、以後のアクセスでも同じセッションが使われ続けます。
2. Spring Securityにおけるセッション管理の基本概念
Spring Security セッション管理の仕組みは、ユーザー認証や権限の制御を行う際に非常に重要です。Spring Securityは、セッションの生成・再利用・終了といった処理を自動で行ってくれます。
デフォルトでは、ログイン成功時にセッションを新しく生成し、セッション固定攻撃(セッションフィクセーション)を防ぐために、セッションIDを強制的に変更します。
これは次のようなコード設定で確認できます。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.anyRequest().authenticated()
)
.formLogin()
.and()
.sessionManagement(session -> session
.sessionFixation().migrateSession() // デフォルト動作
);
return http.build();
}
}
sessionFixation().migrateSession()は、ログイン時にセッションIDを新しく発行して、以前の情報を引き継ぐという安全な動作です。
また、Spring Security セッション管理では以下のようなパターンも設定可能です:
- 常に新しいセッションを作成(
newSession) - セッションを無効化(
none) - 既存のセッションを使う(
changeSessionId)
これらを使い分けることで、アプリケーションの性質に応じたセキュアなセッション管理が可能になります。
また、同時ログイン数の制御やセッションタイムアウトの設定も可能で、次のような記述でログイン上限数を制限できます。
.sessionManagement(session -> session
.maximumSessions(1) // 最大ログイン数を1に制限
.maxSessionsPreventsLogin(true) // すでにログイン中なら新規ログインを拒否
)
こうしたセッション管理の設定は、ユーザーが複数端末で同時ログインするのを防ぎたい場合や、不正利用を防止したいときに有効です。
実際にセッションがどのように動作しているかを確認するには、ブラウザの開発者ツールで「Cookie」の内容を確認するのが良い方法です。
3. セッションの有効時間(タイムアウト)の基本設定
Spring Security タイムアウト設定は、セキュリティを強化するためにとても重要です。セッションの有効期限(タイムアウト)を適切に設定しておくことで、長時間操作されていないログイン状態を自動で終了させることができます。
セッションのタイムアウト設定は、通常はweb.xmlで指定する方法が知られていますが、Spring Boot + Gradle + pleiades環境で構築したSpringプロジェクトでは、以下のようにJavaコード(Servletコンテナの設定)で定義するのが一般的です。
@Configuration
public class SessionConfig {
@Bean
public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> sessionCustomizer() {
return factory -> factory.setSessionTimeout(Duration.ofMinutes(15)); // 15分でタイムアウト
}
}
この設定では、ユーザーが最後にアクセスしてから15分間操作がなかった場合にセッションが無効になります。
このようなタイムアウトは、銀行や管理画面などセキュリティリスクが高い場面では非常に有効です。ただし、ユーザー体験にも関わるので、タイムアウト時間の長さには注意が必要です。
セッションの有効期限を設定しても、実際に動作しているかはブラウザでセッションが切れるタイミングを検証して確認しておくと安心です。
4. Spring Securityでセッションの同時ログイン数を制限する方法
セッション同時ログイン制限は、セキュリティ強化のために有効な手段です。例えば、1つのアカウントを複数人で使い回すことを防ぐ場合などに使われます。
Spring Securityでは、maximumSessions()メソッドを使って、同時ログインできるセッション数を制限できます。以下はその設定例です。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.anyRequest().authenticated()
)
.formLogin()
.and()
.sessionManagement(session -> session
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
);
return http.build();
}
}
maximumSessions(1)は、同じユーザーが同時にログインできるセッションを1つに制限する設定です。
maxSessionsPreventsLogin(true)を指定すると、すでにログインしているユーザーがいる場合に、新しいログインをブロックします。
一方で、falseを指定すると、新しいログインが成功し、古いセッションが無効化されます。
この違いを理解して設定しないと、意図しない挙動につながる可能性があるため、開発者は事前にテスト環境でしっかり確認することが大切です。
また、セッションの制限機能を使うには、Spring Securityのセッションレジストリ(SessionRegistry)が内部で管理される点も理解しておきましょう。
5. セッション無効化の挙動(ログアウトやタイムアウト後の動作)
セッション無効化が発生するタイミングは、主に以下の3つです:
- ユーザーがログアウトボタンを押したとき
- セッションの有効時間を超えて操作がなかったとき(タイムアウト)
- 同時ログイン制限によってセッションが強制終了されたとき
Spring Securityでは、ログアウト時にセッションを無効化する処理が標準で有効になっています。設定は以下のようになります。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.anyRequest().authenticated()
)
.formLogin()
.and()
.logout(logout -> logout
.logoutUrl("/logout") // ログアウトURL
.invalidateHttpSession(true) // セッションを無効化
.deleteCookies("JSESSIONID") // Cookie削除
);
return http.build();
}
}
invalidateHttpSession(true)を指定することで、ログアウト時にセッションが完全に破棄されます。また、deleteCookies("JSESSIONID")により、ブラウザ側のセッションIDも削除されるため、セキュリティ面で安心です。
これにより、ログアウト後にブラウザの「戻るボタン」で前のページに戻っても、再びログインが必要になる状態を確保できます。
なお、セッションがタイムアウトした場合も、内部的にはセッション無効化と同じ扱いになります。そのため、再度ログインしないと保護されたページにはアクセスできません。
特に管理画面や個人情報を扱うページでは、タイムアウト後の挙動が適切に設計されていることが重要です。ログアウト画面や「ログインセッションが切れました」といった明示的な表示を行うことで、ユーザーにも分かりやすくなります。
6. セッションタイムアウト時のエラー画面(403や再ログイン画面)の制御方法
Spring Security タイムアウトが発生すると、ログイン状態が失われ、保護されたページへアクセスした際にエラー画面(403やログイン画面)へ遷移します。これをユーザーにとって分かりやすく制御することで、操作ミスや混乱を防ぐことができます。
タイムアウト後のリダイレクトをログインページに設定するには、以下のようにexceptionHandling()とinvalidSessionUrl()を組み合わせます。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login") // 独自ログイン画面
.permitAll()
)
.sessionManagement(session -> session
.invalidSessionUrl("/session-expired") // セッション切れ時の遷移先
);
return http.build();
}
}
invalidSessionUrl("/session-expired")は、セッションが無効化された(タイムアウト含む)状態でアクセスされた場合のリダイレクト先を指定します。
このパスに対応するコントローラとHTMLを用意しておくことで、わかりやすい案内が可能です。
@Controller
public class SessionController {
@GetMapping("/session-expired")
public String sessionExpired() {
return "sessionExpired"; // sessionExpired.htmlを表示
}
}
<!-- resources/templates/sessionExpired.html -->
<!DOCTYPE html>
<html>
<head>
<title>セッション切れ</title>
</head>
<body>
<h2>セッションがタイムアウトしました</h2>
<p>再度ログインしてください。</p>
<a href="/login">ログイン画面へ</a>
</body>
</html>
これにより、403エラーではなく、「セッションが切れた」ことをユーザーに明示的に伝えるページを表示できます。
7. 実際の動作確認手順(ログイン後に何分でタイムアウトされるか確認)
設定を正しく行っても、タイムアウトが期待通りに動作しているかを確認するテストは非常に重要です。以下は動作確認の具体的な手順です。
- セッション有効時間(例:15分)を設定してアプリケーションを起動
- ログイン後、任意の画面でそのまま操作を停止(タイマーで時間を測定)
- 15分を経過したら、別の保護されたURL(例:
/user/mypage)へアクセス /session-expiredページにリダイレクトされることを確認
この際、ブラウザのCookie情報(JSESSIONID)を開発者ツールで確認すると、セッションが破棄されているかを目視できます。
特に初心者が混乱しやすいのが、「ログイン直後でも操作ができない」「すぐにログアウトされる」といった状況です。このような現象が発生したら、タイムアウト時間が短すぎたり、ブラウザのキャッシュやクッキー設定が影響している可能性があります。
8. よくあるエラーとその対処法(セッションが即切れる・ログイン状態が保持されないなど)
セッション管理 トラブルシューティングの観点から、初心者がよく遭遇する問題とその対処法をまとめます。
① ログイン直後にセッションが切れる
原因として多いのは、セッション有効時間の設定が極端に短い、もしくはsetSessionTimeout()が設定されていないことです。
以下のように、15分程度の余裕を持った設定に変更しましょう。
@Bean
public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> sessionCustomizer() {
return factory -> factory.setSessionTimeout(Duration.ofMinutes(15));
}
② 毎回ログインしないとページが見れない
クッキーが無効になっていたり、JSESSIONIDがうまく保持されていない可能性があります。
- ブラウザの設定でCookieがブロックされていないか確認
- ログイン後のレスポンスヘッダに
Set-Cookieが含まれているかを確認
③ 開発中、セッションの動作が安定しない
複数のブラウザタブでログイン/ログアウトを繰り返していると、セッションの状態が不安定になることがあります。
このような場合は、すべてのタブを閉じてクッキーをクリアし、再起動して検証するのが効果的です。
④ セッション切れ後に403エラーになる
invalidSessionUrl()の設定が無い場合、セッションが切れると403エラー(アクセス拒否)になることがあります。
対処法として、先述したようにセッション無効時の遷移先を明示的に設定してください。
⑤ タイムアウト後、戻るボタンでページが表示される
これはブラウザのキャッシュによるもので、セッション自体は切れていても画面が表示されてしまうことがあります。
この場合、保護されたページにアクセスするたびにセッションチェックが行われるため、画面が表示されても内部的にはログイン済みではない状態になります。
不正アクセスを防ぐには、キャッシュを無効化するHTTPヘッダーを出力する仕組みも検討しましょう。