The first time a user requests a protected resource, they are prompted for credentials. One of the most common ways to prompt for credentials is to redirect the user to a log in page. A summarized HTTP exchange for an unauthenticated user requesting a protected resource might look like this:
GET / HTTP/1.1
Host: example.com
Cookie: SESSION=91470ce0-3f3c-455b-b7ad-079b02290f7b
HTTP/1.1 302 Found
Location: /login
The user submits their username and password.
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 session fixation attacks.
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.
GET / HTTP/1.1
Host: example.com
Cookie: SESSION=4c66e474-3f5a-43ed-8e48-cc1d8cb1d1c8
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:
The javadoc:org.springframework.security.web.context.HttpSessionSecurityContextRepository[] associates the 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.
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.
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.
- Java
-
public SecurityFilterChain filterChain(HttpSecurity http) { http // ... .securityContext((securityContext) -> securityContext .securityContextRepository(new RequestAttributeSecurityContextRepository()) ); return http.build(); }
- XML
-
<http security-context-repository-ref="contextRepository"> <!-- ... --> </http> <b:bean name="contextRepository" class="org.springframework.security.web.context.RequestAttributeSecurityContextRepository" />
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 RequestAttributeSecurityContextRepository
and HttpSessionSecurityContextRepository
simultaneously.
- Java
-
@Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http // ... .securityContext((securityContext) -> securityContext .securityContextRepository(new DelegatingSecurityContextRepository( new RequestAttributeSecurityContextRepository(), new HttpSessionSecurityContextRepository() )) ); return http.build(); }
- Kotlin
-
@Bean fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { http { // ... securityContext { securityContextRepository = DelegatingSecurityContextRepository( RequestAttributeSecurityContextRepository(), HttpSessionSecurityContextRepository() ) } } return http.build() }
- XML
-
<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. |
The javadoc:org.springframework.security.web.context.SecurityContextPersistenceFilter[] is responsible for persisting the SecurityContext
between requests using the SecurityContextRepository
.
Before running the rest of the application,
SecurityContextPersistenceFilter
loads the SecurityContext
from the SecurityContextRepository
and sets it on the SecurityContextHolder
.
Finally, if the
SecurityContext
has changed, we save the SecurityContext
using the SecurityContextRepository
.
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.
The javadoc:org.springframework.security.web.context.SecurityContextHolderFilter[] is responsible for loading the SecurityContext
between requests using the SecurityContextRepository
.
Before running the rest of the application,
SecurityContextHolderFilter
loads the SecurityContext
from the SecurityContextRepository
and sets it on the SecurityContextHolder
.
Unlike, 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.