此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Security 6.3.3! |
此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Security 6.3.3! |
在最终用户可以登录的应用程序中,他们也应该能够注销。
默认情况下, Spring Security 会建立一个端点,因此不需要额外的代码。/logout
本节的其余部分涵盖了许多供您考虑的使用案例:
-
我想知道何时需要明确允许
/logout
端点 -
我想在用户注销时清除 Cookie、存储和/或缓存
-
我正在使用 OAuth 2.0,并且我想与授权服务器协调注销
-
我正在使用 SAML 2.0,并且我想与身份提供商协调注销
-
我正在使用 CAS,并且我想与身份提供商协调注销
了解 Logout 的体系结构
当您包含 spring-boot-starter-security
依赖项或使用 Comments 时, Spring Security 将添加其注销支持,并且默认情况下同时响应 和 。@EnableWebSecurity
GET /logout
POST /logout
如果请求,则 Spring Security 将显示注销确认页面。
除了为用户提供有价值的双重检查机制外,它还提供了一种简单的方法来向 .GET /logout
POST /logout
请注意,如果在配置中关闭了 CSRF 保护,则不会向用户显示注销确认页面,而是直接执行注销。
在您的应用程序中,无需 来执行注销。
只要请求中存在所需的 CSRF 令牌,您的应用程序就可以简单地诱导注销。GET /logout POST /logout |
如果请求,则它将使用一系列 LogoutHandler
实例执行以下默认操作:POST /logout
-
使 HTTP 会话失效 (
SecurityContextLogoutHandler
) -
清除
SecurityContextHolderStrategy
(SecurityContextLogoutHandler
) -
清理所有 RememberMe 身份验证 (
TokenRememberMeServices
/PersistentTokenRememberMeServices
) -
清除所有已保存的 CSRF 令牌 (
CsrfLogoutHandler
) -
触发 (
LogoutSuccessEventPublishingLogoutHandler
LogoutSuccessEvent
)
完成后,它将执行其默认的 LogoutSuccessHandler
,该处理程序会重定向到 ./login?logout
在您的应用程序中,无需 来执行注销。
只要请求中存在所需的 CSRF 令牌,您的应用程序就可以简单地诱导注销。GET /logout POST /logout |
自定义注销 URI
由于 the 出现在过滤器链中的 AuthorizationFilter
之前,因此默认情况下没有必要显式允许端点。
因此,通常只有您自己创建的自定义注销终端节点才需要访问配置。LogoutFilter
/logout
permitAll
例如,如果你只想更改 Spring Security 匹配的 URI,你可以通过以下方式在 DSL 中执行此操作:logout
-
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 MVC 在 Spring Security 之后处理您的请求。
您可以使用 or like so 来执行此操作: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>
在此示例中,您告诉 to 重定向到 完成时。
并且,您可以在 AuthorizationFilter
中明确允许终端节点。LogoutFilter
/my/success/endpoint
/my/success/endpoint
但是,指定两次可能会很麻烦。
如果您使用的是 Java 配置,则可以在 logout 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 配置,则可以通过在 DSL 中调用该方法来添加自己的清理操作,如下所示:addLogoutHandler
logout
-
Java
-
Kotlin
CookieClearingLogoutHandler cookies = new CookieClearingLogoutHandler("our-custom-cookie");
http
.logout((logout) -> logout.addLogoutHandler(cookies))
http {
logout {
addLogoutHandler(CookieClearingLogoutHandler("our-custom-cookie"))
}
}
因为 LogoutHandler 实例用于清理,所以它们不应该引发异常。 |
由于 LogoutHandler 是一个功能接口,因此您可以提供自定义接口作为 lambda。 |
一些 logout 处理程序配置非常常见,以至于它们直接在 DSL 和 element 中公开。
一个示例是配置会话失效,另一个示例是应删除哪些其他 Cookie。logout
<logout>
例如,您可以配置 CookieClearingLogoutHandler
,如上所示。
-
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>
指定 cookie 不是必需的,因为 SecurityContextLogoutHandler 会通过使会话无效来删除它。JSESSIONID |
使用 clear-site-data 注销用户
HTTP 标头是浏览器支持作为清除属于所属网站的 Cookie、存储和缓存的指令的标头。
这是一种方便且安全的方法,可确保在注销时清理所有内容(包括会话 Cookie)。Clear-Site-Data
您可以添加配置 Spring Security 以在注销时写入 Headers,如下所示:Clear-Site-Data
-
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)
}
}
因为 LogoutHandler 实例用于清理,所以它们不应该引发异常。 |
由于 LogoutHandler 是一个功能接口,因此您可以提供自定义接口作为 lambda。 |
指定 cookie 不是必需的,因为 SecurityContextLogoutHandler 会通过使会话无效来删除它。JSESSIONID |
自定义注销成功
虽然在大多数情况下,使用 就足够了,但您可能需要执行与注销完成后重定向到 URL 不同的操作。LogoutSuccessHandler
是用于自定义注销成功操作的 Spring Security 组件。logoutSuccessUrl
例如,您可能希望只返回状态代码,而不是重定向。 在这种情况下,您可以提供 success 处理程序实例,如下所示:
-
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 是一个函数接口,因此您可以提供自定义接口作为 lambda。 |
由于 LogoutSuccessHandler 是一个函数接口,因此您可以提供自定义接口作为 lambda。 |
创建自定义注销终端节点
强烈建议您使用提供的 DSL 来配置注销。
原因之一是很容易忘记调用所需的 Spring Security 组件以确保正确和完整的注销。logout
事实上,注册自定义LogoutHandler
通常比创建用于执行注销的 Spring MVC 端点更简单。
也就是说,如果您发现自己处于需要自定义注销终端节点的情况,如下所示:
-
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
,以确保安全、完整地注销。
至少需要如下内容:
-
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"
}
此外,您还需要明确允许终端节点。
未能调用 SecurityContextLogoutHandler 意味着 SecurityContext 在后续请求中仍然可用,这意味着用户实际上并未注销。 |
未能调用 SecurityContextLogoutHandler 意味着 SecurityContext 在后续请求中仍然可用,这意味着用户实际上并未注销。 |
测试注销
配置注销后,您可以使用 Spring Security 的 MockMvc 支持对其进行测试。
更多与 Logout 相关的参考
-
在 CSRF 注意事项 一节中注销
-
部分 Single Logout (CAS protocol)
-
Spring Security XML Namespace 部分中的 logout 元素的文档