跳到主要内容
版本:7.0.2

处理登出

DeepSeek V3 中英对照 Logout Handling Logouts

在允许终端用户登录的应用程序中,他们也应该能够注销。

默认情况下,Spring Security 会启动一个 /logout 端点,因此无需编写额外代码。

本节其余部分涵盖了一些供您参考的用例:

理解 Logout 的架构

当你引入 spring-boot-starter-security 依赖 或使用 @EnableWebSecurity 注解时,Spring Security 将添加其登出支持,并默认响应 GET /logoutPOST /logout 请求。

当你请求 GET /logout 时,Spring Security 会显示一个登出确认页面。除了为用户提供一个有价值的二次确认机制外,它还提供了一种简单的方法,将所需的 CSRF 令牌传递给 POST /logout

请注意,如果在配置中禁用了CSRF保护,则不会向用户显示注销确认页面,而是直接执行注销操作。

提示

在你的应用中,无需使用 GET /logout 来执行登出操作。只要请求中包含所需的 CSRF 令牌,你的应用可以直接通过 POST /logout 来触发登出。

如果你请求 POST /logout,它将通过一系列 LogoutHandler 实例执行以下默认操作:

完成后,它将执行默认的 LogoutSuccessHandler,该处理器会将用户重定向到 /login?logout

自定义登出URI

由于 LogoutFilter过滤器链中位于AuthorizationFilter之前,因此默认情况下无需显式允许 /logout 端点。因此,通常只有您自己创建的自定义注销端点才需要配置 permitAll 才能被访问。

例如,如果您只想简单地更改Spring Security匹配的URI,可以通过以下方式在logout DSL中实现:

http
.logout((logout) -> logout.logoutUrl("/my/logout/uri"))

并且无需进行授权更改,因为它只是调整了 LogoutFilter

然而,如果您自行搭建了登出成功端点(或在极少数情况下,搭建自定义登出端点),例如使用 Spring MVC,则需要在 Spring Security 中将其设为允许访问。这是因为 Spring MVC 处理请求的时机晚于 Spring Security。

你可以使用 authorizeHttpRequests<intercept-url> 来实现,例如:

http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/my/success/endpoint").permitAll()
// ...
)
.logout((logout) -> logout.logoutSuccessUrl("/my/success/endpoint"))

在此示例中,你告诉 LogoutFilter 在完成后重定向到 /my/success/endpoint。同时,你需要在 AuthorizationFilter 中明确允许 /my/success/endpoint 端点。

不过,指定两次可能会很繁琐。如果你使用的是 Java 配置,可以在注销 DSL 中设置 permitAll 属性,如下所示:

http
.authorizeHttpRequests((authorize) -> authorize
// ...
)
.logout((logout) -> logout
.logoutSuccessUrl("/my/success/endpoint")
.permitAll()
)

这将为您将所有登出URI添加到许可列表中。

添加清理操作

如果你正在使用Java配置,可以通过在logout DSL中调用addLogoutHandler方法添加自定义的清理操作,如下所示:

CookieClearingLogoutHandler cookies = new CookieClearingLogoutHandler("our-custom-cookie");
http
.logout((logout) -> logout.addLogoutHandler(cookies))
备注

由于 LogoutHandler 实例的用途是进行清理工作,因此它们不应抛出异常。

提示

由于 LogoutHandler 是一个函数式接口,你可以通过 lambda 表达式提供自定义的实现。

一些注销处理程序的配置非常常见,因此它们直接在 logout DSL 和 <logout> 元素中暴露出来。一个例子是配置会话失效,另一个例子是应该删除哪些额外的 cookie。

例如,您可以如上所示配置 CookieClearingLogoutHandler

或者,你也可以通过如下方式设置相应的配置值:

http
.logout((logout) -> logout.deleteCookies("our-custom-cookie"))
备注

指定 JSESSIONID cookie 并非必需,因为 SecurityContextLogoutHandler 会通过使会话失效来移除它。

使用 Clear-Site-Data 登出用户

Clear-Site-Data HTTP 头是浏览器支持的一种指令,用于清除属于所属网站的 cookie、存储和缓存。这是一种便捷且安全的方式,可确保在注销时清理所有内容,包括会话 cookie。

你可以配置 Spring Security 在登出时写入 Clear-Site-Data 头部,如下所示:

HeaderWriterLogoutHandler clearSiteData = new HeaderWriterLogoutHandler(new ClearSiteDataHeaderWriter(Directives.ALL));
http
.logout((logout) -> logout.addLogoutHandler(clearSiteData))

你向 ClearSiteDataHeaderWriter 构造函数传入你想要清除的数据项列表。

上述配置会清除所有站点数据,但你也可以像这样配置仅移除 Cookie:

HeaderWriterLogoutHandler clearSiteData = new HeaderWriterLogoutHandler(new ClearSiteDataHeaderWriter(Directive.COOKIES));
http
.logout((logout) -> logout.addLogoutHandler(clearSiteData))

自定义登出成功处理

虽然 logoutSuccessUrl 在大多数情况下已足够使用,但您可能需要在注销完成后执行除重定向到 URL 以外的其他操作。LogoutSuccessHandler 是 Spring Security 中用于自定义注销成功操作的组件。

例如,您可能希望仅返回状态码而非重定向。在这种情况下,您可以提供一个成功处理器的实例,如下所示:

http
.logout((logout) -> logout.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler()))
提示

由于 LogoutSuccessHandler 是一个函数式接口,你可以通过 lambda 表达式提供自定义的实现。

创建自定义登出端点

强烈建议您使用提供的 logout DSL 来配置注销功能。其中一个原因是,很容易忘记调用必要的 Spring Security 组件,以确保正确且完整的注销过程。

实际上,注册自定义的 LogoutHandler 通常比创建用于执行注销的 Spring MVC 端点更简单。

话虽如此,如果你发现自己处于需要自定义登出端点的情况,例如以下这个:

@PostMapping("/my/logout")
public String performLogout() {
// .. perform logout
return "redirect:/home";
}

那么您需要让该端点调用 Spring Security 的 SecurityContextLogoutHandler,以确保安全且完整的注销。至少需要类似以下代码:

SecurityContextLogoutHandler logoutHandler = new SecurityContextLogoutHandler();

@PostMapping("/my/logout")
public String performLogout(Authentication authentication, HttpServletRequest request, HttpServletResponse response) {
// .. perform logout
this.logoutHandler.logout(request, response, authentication);
return "redirect:/home";
}

此外,您还需要明确允许端点

注意

如果未调用 SecurityContextLogoutHandler,则意味着 SecurityContext 在后续请求中可能仍然可用,这表示用户实际上并未被登出。

测试登出功能

配置好登出功能后,你可以使用 Spring Security 的 MockMvc 支持 来测试它。

更多与登出相关的参考资料