|
| 1 | +[[persistant]] |
| 2 | += Persisting Authentication |
| 3 | +:figures: servlet/authentication |
| 4 | + |
| 5 | +The first time a user requests a protected resource, they are xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[prompted for credentials]. |
| 6 | +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]. |
| 7 | +A summarized HTTP exchange for an unauthenticated user requesting a protected resource might look like this: |
| 8 | + |
| 9 | +.Unauthenticated User Requests Protected Resource |
| 10 | +==== |
| 11 | +[source,http] |
| 12 | +---- |
| 13 | +GET / HTTP/1.1 |
| 14 | +Host: example.com |
| 15 | +Cookie: SESSION=91470ce0-3f3c-455b-b7ad-079b02290f7b |
| 16 | +---- |
| 17 | +
|
| 18 | +[source,http] |
| 19 | +---- |
| 20 | +HTTP/1.1 302 Found |
| 21 | +Location: /login |
| 22 | +---- |
| 23 | +==== |
| 24 | + |
| 25 | +The user submits their username and password. |
| 26 | + |
| 27 | +.Username and Password Submitted |
| 28 | +==== |
| 29 | +[source,http] |
| 30 | +---- |
| 31 | +POST /login HTTP/1.1 |
| 32 | +Host: example.com |
| 33 | +Cookie: SESSION=91470ce0-3f3c-455b-b7ad-079b02290f7b |
| 34 | +
|
| 35 | +username=user&password=password&_csrf=35942e65-a172-4cd4-a1d4-d16a51147b3e |
| 36 | +---- |
| 37 | +==== |
| 38 | + |
| 39 | +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]. |
| 40 | + |
| 41 | +.Authenticated User is Associated to New Session |
| 42 | +==== |
| 43 | +[source,http] |
| 44 | +---- |
| 45 | +HTTP/1.1 302 Found |
| 46 | +Location: / |
| 47 | +Set-Cookie: SESSION=4c66e474-3f5a-43ed-8e48-cc1d8cb1d1c8; Path=/; HttpOnly; SameSite=Lax |
| 48 | +---- |
| 49 | +==== |
| 50 | + |
| 51 | +Subsequent requests include the session cookie which is used to authenticate the user for the remainder of the session. |
| 52 | + |
| 53 | +.Authenticated Session Provided as Credentials |
| 54 | +==== |
| 55 | +[source,http] |
| 56 | +---- |
| 57 | +GET / HTTP/1.1 |
| 58 | +Host: example.com |
| 59 | +Cookie: SESSION=4c66e474-3f5a-43ed-8e48-cc1d8cb1d1c8 |
| 60 | +---- |
| 61 | +==== |
| 62 | + |
| 63 | + |
| 64 | +[[securitycontextrepository]] |
| 65 | +== SecurityContextRepository |
| 66 | + |
| 67 | +// FIXME: api documentation |
| 68 | +In Spring Security the association of the user to future requests is made using {security-api-url}org/springframework/security/web/context/SecurityContextRepository.html[`SecurityContextRepository`]. |
| 69 | + |
| 70 | +[[httpsecuritycontextrepository]] |
| 71 | +=== HttpSecurityContextRepository |
| 72 | + |
| 73 | +The default implementation of `SecurityContextRepository` is {security-api-url}org/springframework/security/web/context/HttpSessionSecurityContextRepository.html[`HttpSessionSecurityContextRepository`] which associates the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontext[`SecurityContext`] to the `HttpSession`. |
| 74 | +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. |
| 75 | + |
| 76 | +[[nullsecuritycontextrepository]] |
| 77 | +=== NullSecurityContextRepository |
| 78 | + |
| 79 | +If it is not desirable to associate the `SecurityContext` to an `HttpSession` (i.e. when authenticating with OAuth) the {security-api-url}org/springframework/security/web/context/NullSecurityContextRepository.html[`NullSecurityContextRepository`] is an implementation of `SecurityContextRepository` that does nothing. |
| 80 | + |
| 81 | +[[requestattributesecuritycontextrepository]] |
| 82 | +=== RequestAttributeSecurityContextRepository |
| 83 | + |
| 84 | +The {security-api-url}org/springframework/security/web/context/RequestAttributeSecurityContextRepository.html[`RequestAttributeSecurityContextRepository`] saves the `SecurityContext` as a request attribute to make sure the `SecurityContext` is avaible for a single request that occurs across dispatch types that may clear out the `SecurityContext`. |
| 85 | + |
| 86 | +For example, assume that a client makes a request, is authenticated, and then an error occurs. |
| 87 | +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. |
| 88 | +When the error dispatch is made, there is no `SecurityContext` established. |
| 89 | +This means that the error page cannot use the `SecurityContext` for authorization or displaying the current user unless the `SecurityContext` is persisted somehow. |
| 90 | + |
| 91 | +== SecurityContextPersistenceFilter |
| 92 | + |
| 93 | +The {security-api-url}org/springframework/security/web/context/SecurityContextPersistenceFilter.html[`SecurityContextPersistenceFilter`] is responsible for persisting the `SecurityContext` between requests using the xref::servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`]. |
| 94 | + |
| 95 | +image::{figures}/securitycontextpersistencefilter.png[] |
| 96 | + |
| 97 | +<1> Before running the rest of the application, `SecurityContextPersistenceFilter` loads the `SecurityContext` from the `SecurityContextRepository` and sets it on the `SecurityContextHolder`. |
| 98 | +<2> Next, the application is ran. |
| 99 | +<3> Finally, if the `SecurityContext` has changed, we save the `SecurityContext` using the `SecurityContextPersistenceRepository`. |
| 100 | +This means that when using `SecurityContextPersistenceFilter`, just setting the `SecurityContextHolder` will ensure that the `SecurityContext` is persisted using `SecurityContextRepository`. |
| 101 | + |
| 102 | +In some cases a response is committed and written to the client before the `SecurityContextPersisteneFilter` method completes. |
| 103 | +For example, if a redirect is sent to the client the response is immediately written back to the client. |
| 104 | +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. |
| 105 | +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. |
| 106 | + |
| 107 | +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. |
0 commit comments