123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- [[persistant]]
- = Persisting Authentication
- :figures: servlet/authentication
- The first time a user requests a protected resource, they are xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[prompted for credentials].
- One of the most common ways to prompt for credentials is to redirect the user to a xref:servlet/authentication/passwords/form.adoc[log in page].
- A summarized HTTP exchange for an unauthenticated user requesting a protected resource might look like this:
- .Unauthenticated User Requests Protected Resource
- ====
- [source,http]
- ----
- GET / HTTP/1.1
- Host: example.com
- Cookie: SESSION=91470ce0-3f3c-455b-b7ad-079b02290f7b
- ----
- [source,http]
- ----
- HTTP/1.1 302 Found
- Location: /login
- ----
- ====
- The user submits their username and password.
- .Username and Password Submitted
- [source,http]
- ----
- POST /login HTTP/1.1
- Host: example.com
- Cookie: SESSION=91470ce0-3f3c-455b-b7ad-079b02290f7b
- username=user&password=password&_csrf=35942e65-a172-4cd4-a1d4-d16a51147b3e
- ----
- Upon authenticating the user, the user is associated to a new session id to prevent xref:servlet/authentication/session-management.adoc#ns-session-fixation[session fixation attacks].
- .Authenticated User is Associated to New Session
- [source,http]
- ----
- HTTP/1.1 302 Found
- Location: /
- Set-Cookie: SESSION=4c66e474-3f5a-43ed-8e48-cc1d8cb1d1c8; Path=/; HttpOnly; SameSite=Lax
- ----
- Subsequent requests include the session cookie which is used to authenticate the user for the remainder of the session.
- .Authenticated Session Provided as Credentials
- [source,http]
- ----
- GET / HTTP/1.1
- Host: example.com
- Cookie: SESSION=4c66e474-3f5a-43ed-8e48-cc1d8cb1d1c8
- ----
- [[securitycontextrepository]]
- == SecurityContextRepository
- // FIXME: api documentation
- In Spring Security the association of the user to future requests is made using javadoc:org.springframework.security.web.context.SecurityContextRepository[].
- The default implementation of `SecurityContextRepository` is javadoc:org.springframework.security.web.context.DelegatingSecurityContextRepository[] which delegates to the following:
- * <<httpsecuritycontextrepository,`HttpSessionSecurityContextRepository`>>
- * <<requestattributesecuritycontextrepository,`RequestAttributeSecurityContextRepository`>>
- [[httpsecuritycontextrepository]]
- === HttpSessionSecurityContextRepository
- The javadoc:org.springframework.security.web.context.HttpSessionSecurityContextRepository[] associates the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontext[`SecurityContext`] to the `HttpSession`.
- Users can replace `HttpSessionSecurityContextRepository` with another implementation of `SecurityContextRepository` if they wish to associate the user with subsequent requests in another way or not at all.
- [[nullsecuritycontextrepository]]
- === NullSecurityContextRepository
- If it is not desirable to associate the `SecurityContext` to an `HttpSession` (i.e. when authenticating with OAuth) the javadoc:org.springframework.security.web.context.NullSecurityContextRepository[] is an implementation of `SecurityContextRepository` that does nothing.
- [[requestattributesecuritycontextrepository]]
- === RequestAttributeSecurityContextRepository
- The javadoc:org.springframework.security.web.context.RequestAttributeSecurityContextRepository[] saves the `SecurityContext` as a request attribute to make sure the `SecurityContext` is available for a single request that occurs across dispatch types that may clear out the `SecurityContext`.
- For example, assume that a client makes a request, is authenticated, and then an error occurs.
- Depending on the servlet container implementation, the error means that any `SecurityContext` that was established is cleared out and then the error dispatch is made.
- When the error dispatch is made, there is no `SecurityContext` established.
- This means that the error page cannot use the `SecurityContext` for authorization or displaying the current user unless the `SecurityContext` is persisted somehow.
- .Use RequestAttributeSecurityContextRepository
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- public SecurityFilterChain filterChain(HttpSecurity http) {
- http
- // ...
- .securityContext((securityContext) -> securityContext
- .securityContextRepository(new RequestAttributeSecurityContextRepository())
- );
- return http.build();
- }
- ----
- XML::
- +
- [source,xml,role="secondary"]
- ----
- <http security-context-repository-ref="contextRepository">
- <!-- ... -->
- </http>
- <b:bean name="contextRepository"
- class="org.springframework.security.web.context.RequestAttributeSecurityContextRepository" />
- ----
- ======
- [[delegatingsecuritycontextrepository]]
- === DelegatingSecurityContextRepository
- The javadoc:org.springframework.security.web.context.DelegatingSecurityContextRepository[] saves the `SecurityContext` to multiple `SecurityContextRepository` delegates and allows retrieval from any of the delegates in a specified order.
- The most useful arrangement for this is configured with the following example, which allows the use of both xref:requestattributesecuritycontextrepository[`RequestAttributeSecurityContextRepository`] and xref:httpsecuritycontextrepository[`HttpSessionSecurityContextRepository`] simultaneously.
- .Configure DelegatingSecurityContextRepository
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- @Bean
- public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
- http
- // ...
- .securityContext((securityContext) -> securityContext
- .securityContextRepository(new DelegatingSecurityContextRepository(
- new RequestAttributeSecurityContextRepository(),
- new HttpSessionSecurityContextRepository()
- ))
- );
- return http.build();
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- @Bean
- fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
- http {
- // ...
- securityContext {
- securityContextRepository = DelegatingSecurityContextRepository(
- RequestAttributeSecurityContextRepository(),
- HttpSessionSecurityContextRepository()
- )
- }
- }
- return http.build()
- }
- ----
- XML::
- +
- [source,xml,role="secondary"]
- ----
- <http security-context-repository-ref="contextRepository">
- <!-- ... -->
- </http>
- <bean name="contextRepository"
- class="org.springframework.security.web.context.DelegatingSecurityContextRepository">
- <constructor-arg>
- <bean class="org.springframework.security.web.context.RequestAttributeSecurityContextRepository" />
- </constructor-arg>
- <constructor-arg>
- <bean class="org.springframework.security.web.context.HttpSessionSecurityContextRepository" />
- </constructor-arg>
- </bean>
- ----
- ======
- [NOTE]
- ====
- In Spring Security 6, the example shown above is the default configuration.
- ====
- [[securitycontextpersistencefilter]]
- == SecurityContextPersistenceFilter
- The javadoc:org.springframework.security.web.context.SecurityContextPersistenceFilter[] is responsible for persisting the `SecurityContext` between requests using the xref::servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`].
- image::{figures}/securitycontextpersistencefilter.png[]
- image:{icondir}/number_1.png[] Before running the rest of the application, `SecurityContextPersistenceFilter` loads the `SecurityContext` from the `SecurityContextRepository` and sets it on the `SecurityContextHolder`.
- image:{icondir}/number_2.png[] Next, the application is ran.
- image:{icondir}/number_3.png[] Finally, if the `SecurityContext` has changed, we save the `SecurityContext` using the `SecurityContextPersistenceRepository`.
- This means that when using `SecurityContextPersistenceFilter`, just setting the `SecurityContextHolder` will ensure that the `SecurityContext` is persisted using `SecurityContextRepository`.
- In some cases a response is committed and written to the client before the `SecurityContextPersistenceFilter` method completes.
- For example, if a redirect is sent to the client the response is immediately written back to the client.
- This means that establishing an `HttpSession` would not be possible in step 3 because the session id could not be included in the already written response.
- Another situation that can happen is that if a client authenticates successfully, the response is committed before `SecurityContextPersistenceFilter` completes, and the client makes a second request before the `SecurityContextPersistenceFilter` completes the wrong authentication could be present in the second request.
- To avoid these problems, the `SecurityContextPersistenceFilter` wraps both the `HttpServletRequest` and the `HttpServletResponse` to detect if the `SecurityContext` has changed and if so save the `SecurityContext` just before the response is committed.
- [[securitycontextholderfilter]]
- == SecurityContextHolderFilter
- The javadoc:org.springframework.security.web.context.SecurityContextHolderFilter[] is responsible for loading the `SecurityContext` between requests using the xref::servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`].
- image::{figures}/securitycontextholderfilter.png[]
- image:{icondir}/number_1.png[] Before running the rest of the application, `SecurityContextHolderFilter` loads the `SecurityContext` from the `SecurityContextRepository` and sets it on the `SecurityContextHolder`.
- image:{icondir}/number_2.png[] Next, the application is ran.
- Unlike, xref:servlet/authentication/persistence.adoc#securitycontextpersistencefilter[`SecurityContextPersistenceFilter`], `SecurityContextHolderFilter` only loads the `SecurityContext` it does not save the `SecurityContext`.
- This means that when using `SecurityContextHolderFilter`, it is required that the `SecurityContext` is explicitly saved.
- include::partial$servlet/architecture/security-context-explicit.adoc[]
|