spring-security/docs/modules/ROOT/pages/servlet/authentication/logout.adoc

409 lines
15 KiB
Plaintext
Raw Normal View History

2019-09-22 02:56:30 -04:00
[[jc-logout]]
= Handling Logouts
2019-09-22 02:56:30 -04:00
2023-04-17 18:44:24 -04:00
In an application where end users can xref:servlet/authentication/index.adoc[login], they should also be able to logout.
2023-04-17 18:44:24 -04:00
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,understand logout's architecture>>
* I want to <<customizing-logout-uris, customize the logout or logout success URI>>
* I want to know when I need to <<permit-logout-endpoints, explicitly permit the `/logout` endpoint>>
* I want to <<clear-all-site-data, clear cookies, storage, and/or cache>> when the user logs out
* I am using OAuth 2.0 and I want to xref:servlet/oauth2/login/advanced.adoc#oauth2login-advanced-oidc-logout[coordinate logout with an Authorization Server]
* I am using SAML 2.0 and I want to xref:servlet/saml2/logout.adoc[coordinate logout with an Identity Provider]
* I am using CAS and I want to xref:servlet/authentication/cas.adoc#cas-singlelogout[coordinate logout with an Identity Provider]
[[logout-architecture]]
2019-09-22 02:56:30 -04:00
[[logout-java-configuration]]
2023-04-17 18:44:24 -04:00
== Understanding Logout's Architecture
2023-04-18 13:33:48 -04:00
When you include {spring-boot-reference-url}using.html#using.build-systems.starters[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`.
2023-04-17 18:44:24 -04:00
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 xref:servlet/exploits/csrf.adoc[the needed CSRF token] to `POST /logout`.
[TIP]
In your application it is not necessary to use `GET /logout` to perform a logout.
So long as xref:servlet/exploits/csrf.adoc[the needed CSRF token] is present in the request, your application can simply `POST /logout` to induce a logout.
2019-09-22 02:56:30 -04:00
2023-04-17 18:44:24 -04:00
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:
2019-09-22 02:56:30 -04:00
2023-04-17 18:44:24 -04:00
- Invalidate the HTTP session ({security-api-url}org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.html[`SecurityContextLogoutHandler`])
- Clear the xref:servlet/authentication/session-management.adoc#use-securitycontextholderstrategy[`SecurityContextHolderStrategy`] ({security-api-url}org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.html[`SecurityContextLogoutHandler`])
- Clear the xref:servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`] ({security-api-url}org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.html[`SecurityContextLogoutHandler`])
- Clean up any xref:servlet/authentication/rememberme.adoc[RememberMe authentication] (`TokenRememberMeServices` / `PersistentTokenRememberMeServices`)
- Clear out any saved xref:servlet/exploits/csrf.adoc[CSRF token] ({security-api-url}org/springframework/security/web/csrf/CsrfLogoutHandler.html[`CsrfLogoutHandler`])
- xref:servlet/authentication/events.adoc[Fire] a `LogoutSuccessEvent` ({security-api-url}org/springframework/security/web/authentication/logout/LogoutSuccessEventPublishingLogoutHandler.html[`LogoutSuccessEventPublishingLogoutHandler`])
2019-09-22 02:56:30 -04:00
2023-04-17 18:44:24 -04:00
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`.
2019-09-22 02:56:30 -04:00
2023-04-17 18:44:24 -04:00
[[customizing-logout-uris]]
== Customizing Logout URIs
Since the `LogoutFilter` appears before xref:servlet/authorization/authorize-http-requests.adoc[the `AuthorizationFilter`] in xref:servlet/architecture.adoc#servlet-filterchain-figure[the filter chain], it is not necessary by default to explicitly permit the `/logout` endpoint.
Thus, only <<permit-logout-endpoints,custom logout endpoints>> that you create yourself generally require a `permitAll` configuration to be reachable.
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:
.Custom Logout Uri
====
.Java
[source,java,role="primary"]
2019-09-22 02:56:30 -04:00
----
2023-04-17 18:44:24 -04:00
http
.logout((logout) -> logout.logoutUrl("/my/logout/uri"))
----
.Kotlin
[source,kotlin,role="secondary"]
----
http {
logout {
logoutUrl = "/my/logout/uri"
}
2019-09-22 02:56:30 -04:00
}
----
2023-04-17 18:44:24 -04:00
.Xml
[source,xml,role="secondary"]
----
<logout logout-url="/my/logout/uri"/>
----
====
and no authorization changes are necessary since it simply adjusts the `LogoutFilter`.
[[permit-logout-endpoints]]
2023-04-18 13:23:57 -04:00
However, if you stand up your own logout success endpoint (or in a rare case, <<creating-custom-logout-endpoint, your own logout endpoint>>), say using {spring-framework-reference-url}web.html#spring-web[Spring MVC], you will need permit it in Spring Security.
2023-04-17 18:44:24 -04:00
This is because Spring MVC processes your request after Spring Security does.
You can do this using `authorizeHttpRequests` or `<intercept-url>` like so:
.Custom Logout Endpoint
====
.Java
[source,java,role="primary"]
----
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/my/success/endpoint").permitAll()
// ...
)
.logout((logout) -> logout.logoutSuccessUrl("/my/success/endpoint"))
----
.Kotlin
[source,kotlin,role="secondary"]
----
http {
authorizeHttpRequests {
authorize("/my/success/endpoint", permitAll)
}
logout {
logoutSuccessUrl = "/my/success/endpoint"
}
}
----
.Xml
[source,xml,role="secondary"]
----
<http>
<filter-url pattern="/my/success/endpoint" access="permitAll"/>
<logout logout-success-url="/my/success/endpoint"/>
</http>
----
====
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 xref:servlet/authorization/authorize-http-requests.adoc[the `AuthorizationFilter`].
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:
.Permitting Custom Logout Endpoints
====
.Java
[source,java,role="primary"]
----
http
.authorizeHttpRequests((authorize) -> authorize
// ...
)
.logout((logout) -> logout
.logoutSuccessUrl("/my/success/endpoint")
.permitAll()
)
----
.Kotlin
[source,kotlin,role="secondary"]
----
http
authorizeHttpRequests {
// ...
}
logout {
logoutSuccessUrl = "/my/success/endpoint"
permitAll = true
}
----
====
which will add all logout URIs to the permit list for you.
[[add-logout-handler]]
== Adding Clean-up Actions
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:
.Custom Logout Handler
====
.Java
[source,java,role="primary"]
----
CookieClearingLogoutHandler cookies = new CookieClearingLogoutHandler("our-custom-cookie");
http
.logout((logout) -> logout.addLogoutHandler(cookies))
----
.Kotlin
[source,kotlin,role="secondary"]
----
http {
logout {
addLogoutHandler(CookieClearingLogoutHandler("our-custom-cookie"))
}
}
----
====
[NOTE]
Because {security-api-url}org/springframework/security/web/authentication/logout/LogoutHandler.html[``LogoutHandler``]s are for the purposes of cleanup, they should not throw exceptions.
[TIP]
Since {security-api-url}org/springframework/security/web/authentication/logout/LogoutHandler.html[`LogoutHandler`] is a functional interface, you can provide a custom one as a lambda.
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.
For example, you can configure the {security-api-url}org/springframework/security/web/authentication/logout/CookieClearingLogoutHandler.html[`CookieClearingLogoutHandler`] as seen above.
[[delete-cookies]]
Or you can instead set the appropriate configuration value like so:
====
.Java
[source,java,role="primary"]
----
http
.logout((logout) -> logout.deleteCookies("our-custom-cookie"))
----
.Kotlin
[source,kotlin,role="secondary"]
2023-04-17 18:44:24 -04:00
----
http {
logout {
deleteCookies = "our-custom-cookie"
}
}
2023-04-17 18:44:24 -04:00
----
.Xml
[source,kotlin,role="secondary"]
----
<http>
<logout delete-cookies="our-custom-cookie"/>
</http>
----
====
2019-09-22 02:56:30 -04:00
[NOTE]
2023-04-17 18:44:24 -04:00
Specifying that the `JSESSIONID` cookie is not necessary since {security-api-url}/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.html[`SecurityContextLogoutHandler`] removes it by virtue of invalidating the session.
[[clear-all-site-data]]
=== Using Clear-Site-Data to Log Out the User
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.
You can add configure Spring Security to write the `Clear-Site-Data` header on logout like so:
.Using Clear-Site-Data
2020-02-28 13:14:41 -05:00
====
2023-04-17 18:44:24 -04:00
.Java
[source,java,role="primary"]
----
HeaderWriterLogoutHandler clearSiteData = new HeaderWriterLogoutHandler(new ClearSiteDataHeaderWriter());
http
.logout((logout) -> logout.addLogoutHandler(clearSiteData))
----
.Kotlin
[source,kotlin,role="secondary"]
----
val clearSiteData = HeaderWriterLogoutHandler(ClearSiteDataHeaderWriter())
http {
logout {
addLogoutHandler(clearSiteData)
}
}
----
2020-02-28 13:14:41 -05:00
====
2019-09-22 02:56:30 -04:00
2023-04-17 18:44:24 -04:00
You give the `ClearSiteDataHeaderWriter` constructor the list of things that you want to be cleared out.
2019-09-22 02:56:30 -04:00
2023-04-17 18:44:24 -04:00
The above configuration clears out all site data, but you can also configure it to remove just cookies like so:
2019-09-22 02:56:30 -04:00
2023-04-17 18:44:24 -04:00
.Using Clear-Site-Data to Clear Cookies
====
.Java
[source,java,role="primary"]
----
HeaderWriterLogoutHandler clearSiteData = new HeaderWriterLogoutHandler(new ClearSiteDataHeaderWriter(Directives.COOKIES));
http
.logout((logout) -> logout.addLogoutHandler(clearSiteData))
----
2019-09-22 02:56:30 -04:00
2023-04-17 18:44:24 -04:00
.Kotlin
[source,kotlin,role="secondary"]
----
val clearSiteData = HeaderWriterLogoutHandler(ClearSiteDataHeaderWriter(Directives.COOKIES))
http {
logout {
addLogoutHandler(clearSiteData)
}
}
----
====
2019-09-22 02:56:30 -04:00
2023-04-17 18:44:24 -04:00
[[customizing-logout-success]]
== Customizing Logout Success
2019-09-22 02:56:30 -04:00
2023-04-17 18:44:24 -04:00
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.
2019-09-22 02:56:30 -04:00
2023-04-17 18:44:24 -04:00
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:
2019-09-22 02:56:30 -04:00
2023-04-17 18:44:24 -04:00
.Using Clear-Site-Data to Clear Cookies
====
.Java
[source,java,role="primary"]
----
http
.logout((logout) -> logout.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler()))
----
2019-09-22 02:56:30 -04:00
2023-04-17 18:44:24 -04:00
.Kotlin
[source,kotlin,role="secondary"]
----
http {
logout {
logoutSuccessHandler = HttpStatusReturningLogoutSuccessHandler()
}
}
----
.Xml
[source,xml,role="secondary"]
----
<bean name="mySuccessHandlerBean" class="org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler"/>
<http>
<logout success-handler-ref="mySuccessHandlerBean"/>
</http>
----
====
[TIP]
Since {security-api-url}org/springframework/security/web/authentication/logout/LogoutSuccessHandler.html[`LogoutSuccessHandler`] is a functional interface, you can provide a custom one as a lambda.
[[creating-custom-logout-endpoint]]
== Creating a Custom Logout Endpoint
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.
2023-04-18 13:23:57 -04:00
In fact, it is often simpler to <<add-logout-handler, register a custom `LogoutHandler`>> than create a {spring-framework-reference-url}web.html#spring-web[Spring MVC] endpoint for performing logout.
2023-04-17 18:44:24 -04:00
That said, if you find yourself in a circumstance where a custom logout endpoint is needed, like the following one:
.Custom Logout Endpoint
====
.Java
[source,java,role="primary"]
----
@PostMapping("/my/logout")
public String performLogout() {
// .. perform logout
return "redirect:/home";
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@PostMapping("/my/logout")
fun performLogout(): String {
// .. perform logout
return "redirect:/home"
}
----
====
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:
.Custom Logout Endpoint
====
.Java
[source,java,role="primary"]
----
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";
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
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"
}
----
====
2019-09-22 02:56:30 -04:00
2023-04-17 18:44:24 -04:00
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.
2019-09-22 02:56:30 -04:00
2023-04-17 18:44:24 -04:00
Also, you'll need to <<permit-logout-endpoints, explicitly permit the endpoint>>.
2019-09-22 02:56:30 -04:00
2023-04-17 18:44:24 -04:00
[WARNING]
Failing to call {security-api-url}/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.html[`SecurityContextLogoutHandler`] means that xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontext[the `SecurityContext`] could still be available on subsequent requests, meaning that the user is not actually logged out.
2019-09-22 02:56:30 -04:00
2023-04-17 18:44:24 -04:00
[[testing-logout]]
== Testing Logout
Once you have logout configured you can test it using xref:servlet/test/mockmvc/logout.adoc[Spring Security's MockMvc support].
2019-09-22 02:56:30 -04:00
[[jc-logout-references]]
== Further Logout-Related References
2019-09-22 02:56:30 -04:00
- xref:servlet/test/mockmvc/logout.adoc#test-logout[Testing Logout]
2023-04-17 18:44:24 -04:00
- xref:servlet/integrations/servlet-api.adoc#servletapi-logout[HttpServletRequest.logout()]
- xref:servlet/authentication/rememberme.adoc#remember-me-impls[Remember-Me Interfaces and Implementations]
2023-05-02 17:08:37 -04:00
- xref:servlet/exploits/csrf.adoc#csrf-considerations-logout[Logging Out] in section CSRF Caveats
2023-04-17 18:44:24 -04:00
- Section xref:servlet/authentication/cas.adoc#cas-singlelogout[Single Logout] (CAS protocol)
- Documentation for the xref:servlet/appendix/namespace/http.adoc#nsa-logout[logout element] in the Spring Security XML Namespace section