1. 简介
Spring Security 是保护 Spring 应用的事实标准,提供了完整的认证与授权机制,涵盖登录、登出、权限控制等核心功能。
本文聚焦于 如何在 Spring Security 中实现手动登出(Manual Logout),并深入几种常见的登出场景处理方式。
⚠️ 前提:假设你已经熟悉 Spring Security 的默认登出流程,本文不再赘述基础配置。
2. 基础登出
当用户触发登出操作时,系统需要清理当前会话状态,核心包括两个动作:
✅ 使 HTTP Session 失效
✅ 清除 SecurityContext
(其中保存了用户的认证信息)
Spring Security 提供了 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(比如 remember-me、tracking ID 等)。
我们可以通过自定义 LogoutHandler
,在登出时遍历并清除所有 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
,可直接使用,无需手写循环逻辑。例如:
.addLogoutHandler(new CookieClearingLogoutHandler("JSESSIONID", "remember-me"))
这样更简洁,也更安全。
4. 使用 Clear-Site-Data Header 登出
除了操作 Cookie,现代浏览器支持通过 HTTP 响应头 Clear-Site-Data
来批量清除站点数据(如 Cookie、缓存、本地存储等)。
这个机制特别适合 SPA 或前后端分离架构,能一站式清理客户端残留状态。
配置方式如下:
@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();
}
}
⚠️ 注意事项:
Clear-Site-Data
是浏览器行为,依赖客户端支持(现代主流浏览器基本都支持)- 出于安全考虑,该 Header 仅在 HTTPS 请求下生效,否则会被忽略
- 如果只清除部分数据(如只清 Cookie 不清 Storage),可能造成状态不一致(Incomplete Clearing),建议全量清除或按需明确指定
5. 基于 HttpServletRequest.logout() 的登出
Servlet 3.1+ 提供了标准 API:HttpServletRequest.logout()
,Spring Security 也对其做了集成支持。
我们可以在 LogoutHandler
中调用该方法,由容器统一处理登出逻辑。
配置示例:
@Configuration
public static class LogoutOnRequestConfiguration {
private static final Logger logger = LoggerFactory.getLogger(LogoutOnRequestConfiguration.class);
@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();
}
📌 说明:
.secure(true)
模拟 HTTPS 请求,避免 Clear-Site-Data 等机制被跳过unauthenticated()
断言用户已登出,SecurityContext 已清空
6. 总结
Spring Security 提供了灵活且强大的登出机制,支持多种场景组合:
✅ SecurityContextLogoutHandler
—— 清认证上下文(默认必选)
✅ CookieClearingLogoutHandler
—— 清 Cookie(适合 remember-me 场景)
✅ Clear-Site-Data Header
—— 客户端全量清理(推荐用于前后端分离)
✅ HttpServletRequest.logout()
—— 调用容器原生登出(兼容性好)
在实际项目中,可以根据需求组合多个 LogoutHandler
,实现干净、彻底的用户登出体验。
示例代码已托管至 GitHub:https://github.com/eugenp/tutorials/tree/master/spring-security-modules/spring-security-web-login-2