1. 引言
Spring Security 是用於安全 Spring 應用的標準解決方案。它提供了多種功能來管理用户身份驗證,包括登錄和註銷。
在本教程中,我們將重點介紹使用 Spring Security 進行手動註銷。
我們假設讀者已經理解了標準 Spring Security 註銷流程。
2. 基本登出
當用户嘗試登出時,其當前會話狀態會產生以下影響。我們需要通過以下兩個步驟銷燬會話:
- 無效化 HTTP 會話信息。
- 清除 SecurityContext,因為它包含身份驗證信息。
SecurityContextLogoutHandler 執行這兩個操作。
讓我們看看它的實際效果:
@Configuration
public class DefaultLogoutConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.logout(logout -> logout
.logoutUrl("/basic/basiclogout")
.addLogoutHandler(new SecurityContextLogoutHandler())
);
return http.build();
}
}請注意,SecurityContextLogoutHandler 是 Spring Security 默認添加的——我們在此處展示它以供參考。
3. Cookie 清理登出
通常,登出操作也需要清除用户的一些或全部 Cookie。
為此,我們可以創建一個 LogoutHandler,該 Handler 遍歷所有 Cookie 並於登出時將其過期:
@Configuration
public class AllCookieClearingLogoutConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.logout(logout -> logout
.logoutUrl("/cookies/cookielogout")
.addLogoutHandler((request, response, auth) -> {
for (Cookie cookie : request.getCookies()) {
String cookieName = cookie.getName();
Cookie cookieToDelete = new Cookie(cookieName, null);
cookieToDelete.setMaxAge(0);
response.addCookie(cookieToDelete);
}
})
);
return http.build();
}
}事實上,Spring Security 提供 CookieClearingLogoutHandler,這是一個用於清除 Cookie 的就緒式註銷處理器。
4. Clear-Site-Data 頭部註銷
同樣,我們可以使用特殊的 HTTP 響應頭來實現相同的功能;這裏 Clear-Site-Data 頭部發揮作用。
基本上,Clear-Data-Site 頭部會清除與請求網站相關的瀏覽數據(cookie、存儲、緩存):
@Configuration
public class ClearSiteDataHeaderLogoutConfiguration {
private static final ClearSiteDataHeaderWriter.Directive[] SOURCE =
{CACHE, COOKIES, STORAGE, EXECUTION_CONTEXTS};
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.logout(logout -> logout
.logoutUrl("/csd/csdlogout")
.addLogoutHandler(new HeaderWriterLogoutHandler(new ClearSiteDataHeaderWriter(SOURCE)))
);
return http.build();
}
}然而,僅清理一種存儲類型可能會導致應用程序狀態損壞。因此,由於不完整清除,該頭部僅在請求安全時才被應用。
5. 請求註銷
類似於地,我們可以使用 HttpServletRequest.logout() 方法手動註銷用户。
首先,讓我們添加必要的配置以在請求上手動調用 .logout() 方法:
@Configuration
public static class LogoutOnRequestConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.securityMatchers("/request/**")
.authorizeHttpRequests(authz -> authz.anyRequest()
.permitAll())
.logout(logout -> logout.logoutUrl("/request/logout")
.addLogoutHandler((request, response, auth) -> {
try {
request.logout();
} catch (ServletException e) {
logger.error(e.getMessage());
}
}));
return http.build();
}
}最後,讓我們創建一個測試用例以確認一切都按預期工作:
@Test
public void givenLoggedUserWhenUserLogoutOnRequestThenSessionCleared() throws Exception {
this.mockMvc.perform(post("/request/logout").secure(true)
.with(csrf()))
.andExpect(status().is3xxRedirection())
.andExpect(unauthenticated())
.andReturn();
}6. 結論
總而言之,Spring Security 提供了許多內置功能來處理身份驗證場景。 熟練掌握如何通過編程方式使用這些功能在實際應用中總是非常方便。