ログアウトの処理
エンドユーザーがログインできるアプリケーションでは、ログアウトもできる必要があります。
デフォルトでは、Spring Security は /logout
エンドポイントを立ち上げるため、追加のコードは必要ありません。
このセクションの残りの部分では、考慮すべき多くの使用例について説明します。
/logout
エンドポイントを明示的に許可する必要がある場合を知りたいユーザーがログアウトするときに Cookie、ストレージ、キャッシュをクリアするしたい
OAuth 2.0 を使用しており、認可サーバーとログアウトを調整したいと考えています
SAML 2.0 を使用しており、アイデンティティプロバイダーとログアウトを調整したいと考えています。
CAS を使用しており、アイデンティティプロバイダーとログアウトを調整したいと考えています。
Logout のアーキテクチャを理解する
spring-boot-starter-security
依存関係を含めるか、@EnableWebSecurity
アノテーションを使用すると、Spring Security はログアウトサポートを追加し、デフォルトで GET /logout
と POST /logout
の両方に応答します。
GET /logout
をリクエストすると、Spring Security はログアウト確認ページを表示します。ユーザーに価値のある二重チェックメカニズムを提供するだけでなく、必要な CSRF トークンを POST /logout
に提供する簡単な方法も提供します。
CSRF の保護が構成で無効になっている場合、ログアウト確認ページはユーザーに表示されず、ログアウトが直接実行されることに注意してください。
アプリケーションでは、ログアウトを実行するために GET /logout を使用する必要はありません。必要な CSRF トークンがリクエスト内に存在する限り、アプリケーションは単純に POST /logout を実行してログアウトを誘導できます。 |
POST /logout
をリクエストすると、一連の LogoutHandler
(Javadoc) インスタンスを使用して次のデフォルト操作が実行されます。
HTTP セッションを無効にする (
SecurityContextLogoutHandler
(Javadoc) )SecurityContextHolderStrategy
をクリア (SecurityContextLogoutHandler
(Javadoc) )SecurityContextRepository
をクリア (SecurityContextLogoutHandler
(Javadoc) )RememberMe 認証をクリーンアップする (
TokenRememberMeServices
/PersistentTokenRememberMeServices
)保存されている CSRF トークンをすべて消去します (
CsrfLogoutHandler
(Javadoc) )火と
LogoutSuccessEvent
(LogoutSuccessEventPublishingLogoutHandler
(Javadoc) )
完了すると、デフォルトの LogoutSuccessHandler
(Javadoc) が実行され、/login?logout
にリダイレクトされます。
ログアウト URI のカスタマイズ
LogoutFilter
はフィルターチェーン内で AuthorizationFilter
の前に現れるため、デフォルトでは /logout
エンドポイントを明示的に認可する必要はありません。通常、到達可能にするために permitAll
構成が必要なのは、自分で作成したカスタムログアウトエンドポイントのみです。
例: Spring Security が一致する URI を単純に変更したい場合は、logout
DSL で次の方法で変更できます。
Java
Kotlin
XML
http
.logout((logout) -> logout.logoutUrl("/my/logout/uri"))
http {
logout {
logoutUrl = "/my/logout/uri"
}
}
<logout logout-url="/my/logout/uri"/>
また、LogoutFilter
を調整するだけなので、権限の変更は必要ありません。
ただし、独自のログアウト成功エンドポイント (または、まれに独自のログアウトエンドポイント ) を立ち上げる場合 (たとえば Spring MVC を使用する場合)、それを Spring Security で許可する必要があります。これは、Spring Security がリクエストを処理した後に Spring MVC がリクエストを処理するためです。
次のように authorizeHttpRequests
または <intercept-url>
を使用してこれを行うことができます。
Java
Kotlin
XML
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/my/success/endpoint").permitAll()
// ...
)
.logout((logout) -> logout.logoutSuccessUrl("/my/success/endpoint"))
http {
authorizeHttpRequests {
authorize("/my/success/endpoint", permitAll)
}
logout {
logoutSuccessUrl = "/my/success/endpoint"
}
}
<http>
<filter-url pattern="/my/success/endpoint" access="permitAll"/>
<logout logout-success-url="/my/success/endpoint"/>
</http>
この例では、完了したら /my/success/endpoint
にリダイレクトするように LogoutFilter
に指示します。また、AuthorizationFilter
で /my/success/endpoint
エンドポイントを明示的に認可します。
ただし、2 回指定すると面倒になる場合があります。Java 構成を使用している場合は、代わりに次のようにログアウト DSL で permitAll
プロパティを設定できます。
Java
Kotlin
http
.authorizeHttpRequests((authorize) -> authorize
// ...
)
.logout((logout) -> logout
.logoutSuccessUrl("/my/success/endpoint")
.permitAll()
)
http
authorizeHttpRequests {
// ...
}
logout {
logoutSuccessUrl = "/my/success/endpoint"
permitAll = true
}
これにより、すべてのログアウト URI が許可リストに追加されます。
クリーンアップアクションの追加
Java 構成を使用している場合は、次のように logout
DSL で addLogoutHandler
メソッドを呼び出すことで、独自のクリーンアップアクションを追加できます。
Java
Kotlin
CookieClearingLogoutHandler cookies = new CookieClearingLogoutHandler("our-custom-cookie");
http
.logout((logout) -> logout.addLogoutHandler(cookies))
http {
logout {
addLogoutHandler(CookieClearingLogoutHandler("our-custom-cookie"))
}
}
LogoutHandler (Javadoc) インスタンスはクリーンアップを目的としているため、例外をスローしてはなりません。 |
LogoutHandler (Javadoc) は関数型インターフェースであるため、カスタムインターフェースをラムダとして提供できます。 |
一部のログアウトハンドラー構成は、logout
DSL および <logout>
要素で直接公開されるほど一般的です。1 つの例はセッションの無効化の構成であり、もう 1 つは追加の Cookie を削除する必要があることです。
例: 上記のように CookieClearingLogoutHandler
(Javadoc) を設定できます。
Java
Kotlin
XML
http
.logout((logout) -> logout.deleteCookies("our-custom-cookie"))
http {
logout {
deleteCookies = "our-custom-cookie"
}
}
<http>
<logout delete-cookies="our-custom-cookie"/>
</http>
SecurityContextLogoutHandler (Javadoc) はセッションを無効にすることで JSESSIONID Cookie を削除するため、JSESSIONID Cookie が必要ないことを指定します。 |
Clear-Site-Data を使用してユーザーをログアウトする
Clear-Site-Data
HTTP ヘッダーは、所有する Web サイトに属する Cookie、ストレージ、キャッシュをクリアする命令としてブラウザーがサポートするヘッダーです。これは、ログアウト時にセッション Cookie を含むすべてが確実にクリーンアップされる便利で安全な方法です。
次のように、ログアウト時に Clear-Site-Data
ヘッダーを書き込むように Spring Security の設定を追加できます。
Java
Kotlin
HeaderWriterLogoutHandler clearSiteData = new HeaderWriterLogoutHandler(new ClearSiteDataHeaderWriter());
http
.logout((logout) -> logout.addLogoutHandler(clearSiteData))
val clearSiteData = HeaderWriterLogoutHandler(ClearSiteDataHeaderWriter())
http {
logout {
addLogoutHandler(clearSiteData)
}
}
ClearSiteDataHeaderWriter
コンストラクターに、消去したいもののリストを渡します。
上記の設定ではすべてのサイトデータが消去されますが、次のように Cookie のみを削除するように設定することもできます。
Java
Kotlin
HeaderWriterLogoutHandler clearSiteData = new HeaderWriterLogoutHandler(new ClearSiteDataHeaderWriter(Directive.COOKIES));
http
.logout((logout) -> logout.addLogoutHandler(clearSiteData))
val clearSiteData = HeaderWriterLogoutHandler(ClearSiteDataHeaderWriter(Directive.COOKIES))
http {
logout {
addLogoutHandler(clearSiteData)
}
}
ログアウト成功のカスタマイズ
ほとんどの場合は logoutSuccessUrl
の使用で十分ですが、ログアウトの完了後に URL へのリダイレクトとは異なる操作が必要になる場合があります。LogoutSuccessHandler
(Javadoc) は、ログアウト成功アクションをカスタマイズするための Spring Security コンポーネントです。
例: リダイレクトの代わりに、ステータスコードのみを返したい場合があります。この場合、次のように成功ハンドラーインスタンスを提供できます。
Java
Kotlin
XML
http
.logout((logout) -> logout.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler()))
http {
logout {
logoutSuccessHandler = HttpStatusReturningLogoutSuccessHandler()
}
}
<bean name="mySuccessHandlerBean" class="org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler"/>
<http>
<logout success-handler-ref="mySuccessHandlerBean"/>
</http>
LogoutSuccessHandler (Javadoc) は関数型インターフェースであるため、カスタムインターフェースをラムダとして提供できます。 |
カスタムログアウトエンドポイントの作成
提供されている logout
DSL を使用してログアウトを構成することを強くお勧めします。理由の 1 つは、適切かつ完全なログアウトを保証するために必要な Spring Security コンポーネントを呼び出すのを忘れやすいためです。
実際、多くの場合、ログアウトを実行するための Spring MVC エンドポイントを作成するよりも、カスタム LogoutHandler
を登録する方が簡単です。
ただし、次のようなカスタムログアウトエンドポイントが必要な状況に陥った場合は、次のようになります。
Java
Kotlin
@PostMapping("/my/logout")
public String performLogout() {
// .. perform logout
return "redirect:/home";
}
@PostMapping("/my/logout")
fun performLogout(): String {
// .. perform logout
return "redirect:/home"
}
次に、安全で完全なログアウトを確保するために、そのエンドポイントで Spring Security の SecurityContextLogoutHandler
(Javadoc) を呼び出す必要があります。少なくとも次のようなものが必要です。
Java
Kotlin
SecurityContextLogoutHandler logoutHandler = new SecurityContextLogoutHandler();
@PostMapping("/my/logout")
public String performLogout(Authentication authentication, HttpServletRequest request, HttpServletResponse response) {
// .. perform logout
this.logoutHandler.doLogout(request, response, authentication);
return "redirect:/home";
}
val logoutHandler = SecurityContextLogoutHandler()
@PostMapping("/my/logout")
fun performLogout(val authentication: Authentication, val request: HttpServletRequest, val response: HttpServletResponse): String {
// .. perform logout
this.logoutHandler.doLogout(request, response, authentication)
return "redirect:/home"
}
これにより、必要に応じて SecurityContextHolderStrategy
(Javadoc) と SecurityContextRepository
(Javadoc) がクリアされます。
また、エンドポイントを明示的に許可する必要があります。
SecurityContextLogoutHandler (Javadoc) の呼び出しに失敗するということは、SecurityContext が後続のリクエストで引き続き使用できる可能性があること、つまりユーザーが実際にはログアウトされていないことを意味します。 |
ログアウトのテスト
ログアウトを構成したら、Spring Security の MockMvc サポートを使用してテストできます。