The main point being made here is that Spring Boot, specifically Spring Security, generates an HttpSession on the backend after successful authentication.
Essentially, the backend maintains session data (authenticated user) on the server side, making it stateful. The client (in this case, a browser) is recognized by the provided session id.
From a protocol perspective, the authentication process looks like this:
Scenario: Browser sends an HTTP request to the backend. The backend responds with Unauthorized
because the client attempts to access a protected resource without the Authorization
header.
$ curl -I -H "X-Requested-With:XMLHttpRequest" http://localhost:8080/user
HTTP/1.1 401
Set-Cookie: XSRF-TOKEN=b1137571-5e15-491c-8df5-9db5d34f29a8;path=/
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Transfer-Encoding: chunked
Scenario: Preemptive basic authentication. The backend allows access to the resource and informs the client about the session id.
$ curl -I -H "X-Requested-With:XMLHttpRequest" -H "Authorization:Basic dXNlcjpwYXNzd29yZA==" http://localhost:8080/user
HTTP/1.1 200
Set-Cookie: XSRF-TOKEN=ef72f0b8-4262-4ea2-8a46-5f7e19558079;path=/
Set-Cookie: JSESSIONID=52B61923DE639EE339A653845FBFC5F2;path=/;HttpOnly
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type : application/json;charset=UTF-8
Content-Length: 343
When the browser includes valid user credentials in the HTTP header Authorization
, the backend establishes a new HttpSession and returns an identifier (session id) to the browser.
Set-Cookie: JSESSIONID=52B61923DE639EE339A653845FBFC5F2;path=/;HttpOnly
Subsequently, the browser automatically appends the session id as a cookie to each following HTTP request.
Cookie: JSESSIONID=52B61923DE639EE339A653845FBFC5F2
This mechanism prevents the need for repeated authentication cycles, as long as the session remains active on the server side.
It's important to note that basic authentication is not exclusive to Spring Boot or Angular. It has been a standardized authentication method on the Internet for many years.
However, bear in mind that basic authentication is considered outdated and discouraged in modern web applications.
Will Spring simply allow the request through since the user is already authenticated, possibly due to setting the Authentication Basic header post form authentication?
In the scenario described above, you can understand why an empty headers
object does not result in rejection.
- The browser (Angular application) proactively sets the
Authorization
header with valid user credentials and sends an HTTP request to a restricted resource
- The server validates the user credentials, creates an HttpSession identified by a session id, and sends it back to the browser
- The browser automatically attaches the session id as a cookie to subsequent HTTP requests
- The server identifies the HTTP request using the session id and accepts the incoming request
If you configure Spring Security not to create sessions on the server, you can use the following:
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
Add this to SecurityConfiguration::configure
.
With this configuration, every call to /resource
will return an HTTP 401 status because there is no longer a session stored on the server. Now, the Angular application must independently set the Authorization
header for each HTTP request.
Although the browser may also automate the inclusion of the Authorization
header, it's crucial to remember that basic authentication is strongly advised against in modern web applications.
The purpose of this example is not to provide a step-by-step guide on implementing sign-in functionality with Spring Boot and Angular. Instead, the focus is on moving from basic sign-in methods towards a single sign-on approach based on OAuth2 and Spring within a microservice architecture.
Do not consider the code presented here as definitive or complete.