Handling Logouts
在最终用户可以 login 的应用程序中,他们还应该能够注销。
In an application where end users can login, they should also be able to logout.
默认情况下,Spring Security 建立一个 /logout
端点,因此不需要其他代码。
By default, Spring Security stands up a /logout
endpoint, so no additional code is necessary.
本节的其余部分涵盖了许多你可能需要考虑的使用案例:
The rest of this section covers a number of use cases for you to consider:
-
I want to logout-java-configuration
-
I want to customizing-logout-uris
-
I want to know when I need to permit-logout-endpoints
-
I want to storage, and/or cache,clear-all-site-data when the user logs out
-
I am using OAuth 2.0 and I want to coordinate logout with an Authorization Server
-
I am using SAML 2.0 and I want to coordinate logout with an Identity Provider
-
I am using CAS and I want to coordinate logout with an Identity Provider
Understanding Logout’s Architecture
当您包含 the spring-boot-starter-security
依赖项 或使用 @EnableWebSecurity
注释时,Spring Security 将添加注销支持,并默认响应 GET /logout
和 POST /logout
.
When you include the spring-boot-starter-security
dependency or use the @EnableWebSecurity
annotation, Spring Security will add its logout support and by default respond both to GET /logout
and POST /logout
.
如果您请求 GET /logout
,则 Spring Security 将显示一个注销确认页面。除了为用户提供一个有价值的双重检查机制之外,它还提供了一种简单的方法来为 POST /logout
提供 the needed CSRF token。
If you request GET /logout
, then Spring Security displays a logout confirmation page.
Aside from providing a valuable double-checking mechanism for the user, it also provides a simple way to provide the needed CSRF token to POST /logout
.
请注意,如果配置中禁用了 CSRF protection,则不会向用户显示注销确认页面并且将直接执行注销。
Please note that if CSRF protection is disabled in configuration, no logout confirmation page is shown to the user and the logout is performed directly.
在你的应用程序中,无需使用 |
In your application it is not necessary to use |
如果您请求 POST /logout
,那么它将使用一系列 {security-api-url}org/springframework/security/web/authentication/logout/LogoutHandler.html[LogoutHandler
] 执行以下默认操作:
If you request POST /logout
, then it will perform the following default operations using a series of {security-api-url}org/springframework/security/web/authentication/logout/LogoutHandler.html[LogoutHandler
]s:
-
Invalidate the HTTP session ({security-api-url}org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.html[
SecurityContextLogoutHandler
]) -
Clear the
SecurityContextHolderStrategy
({security-api-url}org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.html[SecurityContextLogoutHandler
]) -
Clear the
SecurityContextRepository
({security-api-url}org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.html[SecurityContextLogoutHandler
]) -
Clean up any RememberMe authentication (
TokenRememberMeServices
/PersistentTokenRememberMeServices
) -
Clear out any saved CSRF token ({security-api-url}org/springframework/security/web/csrf/CsrfLogoutHandler.html[
CsrfLogoutHandler
]) -
Fire a
LogoutSuccessEvent
({security-api-url}org/springframework/security/web/authentication/logout/LogoutSuccessEventPublishingLogoutHandler.html[LogoutSuccessEventPublishingLogoutHandler
])
完成后,它将执行其默认 {security-api-url}org/springframework/security/web/authentication/logout/LogoutSuccessHandler.html[LogoutSuccessHandler
],它会重定向到 /login?logout
。
Once completed, then it will exercise its default {security-api-url}org/springframework/security/web/authentication/logout/LogoutSuccessHandler.html[LogoutSuccessHandler
] which redirects to /login?logout
.
Customizing Logout URIs
由于 LogoutFilter`在 the filter chain中出现在 the `AuthorizationFilter
之前,因此默认不必显式允许 `/logout`端点。因此,通常只有您自己创建的 custom logout endpoints才需要 `permitAll`配置才能访问。
Since the LogoutFilter
appears before the AuthorizationFilter
in the filter chain, it is not necessary by default to explicitly permit the /logout
endpoint.
Thus, only permit-logout-endpoints that you create yourself generally require a permitAll
configuration to be reachable.
例如,如果你只想更改 Spring Security 正在匹配的 URI,则可以通过以下方式在 logout
DSL 中执行此操作:
For example, if you want to simply change the URI that Spring Security is matching, you can do so in the logout
DSL in following way:
-
Java
-
Kotlin
-
Xml
http
.logout((logout) -> logout.logoutUrl("/my/logout/uri"))
http {
logout {
logoutUrl = "/my/logout/uri"
}
}
<logout logout-url="/my/logout/uri"/>
并且不需要进行任何授权更改,因为它只是调整了 LogoutFilter
。
and no authorization changes are necessary since it simply adjusts the LogoutFilter
.
但是,如果您使用 {spring-framework-reference-url}web.html#spring-web[Spring MVC] 在自己的注销成功端点(或在极少数情况下,your own logout endpoint)中会话,那么您需要在 Spring Security 中允许它。这是因为 Spring MVC 在 Spring Security 处理完您的请求之后才会处理您的请求。
However, if you stand up your own logout success endpoint (or in a rare case, creating-custom-logout-endpoint), say using {spring-framework-reference-url}web.html#spring-web[Spring MVC], you will need to permit it in Spring Security. This is because Spring MVC processes your request after Spring Security does.
你可以像这样使用 authorizeHttpRequests
或 <intercept-url>
来执行此操作:
You can do this using authorizeHttpRequests
or <intercept-url>
like so:
-
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>
在此示例中,你告诉 LogoutFilter
完成时重定向到 /my/success/endpoint
。而且,你明确允许 xref:servlet/authorization/authorize-http-requests.adoc[the AuthorizationFilter
中的 /my/success/endpoint
端点。
In this example, you tell the LogoutFilter
to redirect to /my/success/endpoint
when it is done.
And, you explicitly permit the /my/success/endpoint
endpoint in the AuthorizationFilter
.
虽然多次指定它有点麻烦。如果你正在使用 Java 配置,你可以像这样在登出 DSL 中设置 permitAll
属性:
Specifying it twice can be cumbersome, though.
If you are using Java configuration, you can instead set the permitAll
property in the logout DSL like so:
-
Java
-
Kotlin
http
.authorizeHttpRequests((authorize) -> authorize
// ...
)
.logout((logout) -> logout
.logoutSuccessUrl("/my/success/endpoint")
.permitAll()
)
http
authorizeHttpRequests {
// ...
}
logout {
logoutSuccessUrl = "/my/success/endpoint"
permitAll = true
}
这会为你添加所有登出 URI 到许可名单中。
which will add all logout URIs to the permit list for you.
Adding Clean-up Actions
如果你正在使用 Java 配置,你可以通过像这样在 logout
DSL 中调用 addLogoutHandler
方法自己添加清除操作:
If you are using Java configuration, you can add clean up actions of your own by calling the addLogoutHandler
method in the logout
DSL, like so:
-
Java
-
Kotlin
CookieClearingLogoutHandler cookies = new CookieClearingLogoutHandler("our-custom-cookie");
http
.logout((logout) -> logout.addLogoutHandler(cookies))
http {
logout {
addLogoutHandler(CookieClearingLogoutHandler("our-custom-cookie"))
}
}
由于 {security-api-url}org/springframework/security/web/authentication/logout/LogoutHandler.html[ |
Because {security-api-url}org/springframework/security/web/authentication/logout/LogoutHandler.html[ |
由于 {security-api-url}org/springframework/security/web/authentication/logout/LogoutHandler.html[ |
Since {security-api-url}org/springframework/security/web/authentication/logout/LogoutHandler.html[ |
某些登出处理程序配置很常见,它们直接在 logout
DSL 和 <logout>
元素中公开。配置会话失效和需要删除哪些其他 cookie 就是一个例子。
Some logout handler configurations are common enough that they are exposed directly in the logout
DSL and <logout>
element.
One example is configuring session invalidation and another is which additional cookies should be deleted.
例如,您可以将 {security-api-url}org/springframework/security/web/authentication/logout/CookieClearingLogoutHandler.html[CookieClearingLogoutHandler
] 配置为上文所述。
For example, you can configure the {security-api-url}org/springframework/security/web/authentication/logout/CookieClearingLogoutHandler.html[CookieClearingLogoutHandler
] as seen above.
Or you can instead set the appropriate configuration value like so:
-
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>
指明 |
Specifying that the |
Using Clear-Site-Data to Log Out the User
Clear-Site-Data
HTTP 标头是一个浏览器能够支持的使用说明,用于清除归属于所有者网站的 cookie、存储和缓存。这是确保登出后清除所有内容(包括会话 cookie)的一种便捷且安全的方式。
The Clear-Site-Data
HTTP header is one that browsers support as an instruction to clear cookies, storage, and cache that belong to the owning website.
This is a handy and secure way to ensure that everything, including the session cookie, is cleaned up on logout.
你可以像这样配置 Spring Security 在登出后写入 Clear-Site-Data
标头:
You can add configure Spring Security to write the Clear-Site-Data
header on logout like so:
-
Java
-
Kotlin
HeaderWriterLogoutHandler clearSiteData = new HeaderWriterLogoutHandler(new ClearSiteDataHeaderWriter());
http
.logout((logout) -> logout.addLogoutHandler(clearSiteData))
val clearSiteData = HeaderWriterLogoutHandler(ClearSiteDataHeaderWriter())
http {
logout {
addLogoutHandler(clearSiteData)
}
}
你给 ClearSiteDataHeaderWriter
构造函数想要清除的项目列表。
You give the ClearSiteDataHeaderWriter
constructor the list of things that you want to be cleared out.
上述配置清除所有站点数据,但你也可以配置它仅清除 cookie,类似这样:
The above configuration clears out all site data, but you can also configure it to remove just cookies like so:
-
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)
}
}
Customizing Logout Success
虽然在大多数情况下使用 logoutSuccessUrl
就足够了,但您可能需要采取一些与注销完成后重定向到 URL 不同的操作。{security-api-url}org/springframework/security/web/authentication/logout/LogoutSuccessHandler.html[LogoutSuccessHandler
] 是用于自定义注销成功操作的 Spring Security 组件。
While using logoutSuccessUrl
will suffice for most cases, you may need to do something different from redirecting to a URL once logout is complete.
{security-api-url}org/springframework/security/web/authentication/logout/LogoutSuccessHandler.html[LogoutSuccessHandler
] is the Spring Security component for customizing logout success actions.
例如,你可能不想重定向,而只想要返回一个状态代码。在这种情况下,你可以提供一个成功处理程序实例,如下所示:
For example, instead of redirecting, you may want to only return a status code. In this case, you can provide a success handler instance, like so:
-
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>
由于 {security-api-url}org/springframework/security/web/authentication/logout/LogoutSuccessHandler.html[ |
Since {security-api-url}org/springframework/security/web/authentication/logout/LogoutSuccessHandler.html[ |
Creating a Custom Logout Endpoint
强烈建议你使用提供的 logout
DSL 来配置登出。一个原因是很容易忘记调用必需的 Spring Security 组件来确保正确且完整的登出。
It is strongly recommended that you use the provided logout
DSL to configure logout.
One reason is that its easy to forget to call the needed Spring Security components to ensure a proper and complete logout.
事实上,直接使用 register a custom LogoutHandler
通常比创建 {spring-framework-reference-url}web.html#spring-web[Spring MVC] 端点来执行注销更简单。
In fact, it is often simpler to add-logout-handler than create a {spring-framework-reference-url}web.html#spring-web[Spring MVC] endpoint for performing logout.
也就是说,如果你发现自己处于需要自定义登出终结点的情况下,就像下面的示例:
That said, if you find yourself in a circumstance where a custom logout endpoint is needed, like the following one:
-
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 的 {security-api-url}/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.html[SecurityContextLogoutHandler
] 以确保进行安全且完整的注销。至少需要类似以下内容:
then you will need to have that endpoint invoke Spring Security’s {security-api-url}/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.html[SecurityContextLogoutHandler
] to ensure a secure and complete logout.
Something like the following is needed at a minimum:
-
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"
}
这将清除 {security-api-url}/org/springframework/security/core/context/SecurityContextHolderStrategy.html[SecurityContextHolderStrategy
] 和 {security-api-url}/org/springframework/security/web/context/SecurityContextRepository.html[SecurityContextRepository
](视需要而定)。
Such will clear out the {security-api-url}/org/springframework/security/core/context/SecurityContextHolderStrategy.html[SecurityContextHolderStrategy
] and {security-api-url}/org/springframework/security/web/context/SecurityContextRepository.html[SecurityContextRepository
] as needed.
此外,你需要 explicitly permit the endpoint。
Also, you’ll need to permit-logout-endpoints.
未调用 {security-api-url}/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.html[SecurityContextLogoutHandler
] 意味着 the SecurityContext
仍然可以在后续请求中使用,即用户实际上并未退出。
Failing to call {security-api-url}/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.html[SecurityContextLogoutHandler
] means that the SecurityContext
could still be available on subsequent requests, meaning that the user is not actually logged out.
Testing Logout
配置好注销后,您可以使用 Spring Security’s MockMvc support 来对其进行测试。
Once you have logout configured you can test it using Spring Security’s MockMvc support.
Further Logout-Related References
-
Logging Out in section CSRF Caveats
-
Section Single Logout (CAS protocol)
-
Documentation for the logout element in the Spring Security XML Namespace section