HomeAbout MeContact
Spring Boot
Spring Security Fundamentals
Piotr Szarpak
Piotr Szarpak
March 14, 2024
3 min
Make sure to subscribe to our newsletter and be the first to know the news.

Spring Security: Fundamentals

Spring Security MVC is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.

Entry Point

In a nutshell, Spring Security is injected into the filter chain of the Java Servlet API.

The entry point to the Spring Security is the DelegatingFilterProxy class which is bascially an implementation of the javax.servlet.Filter interface, and which delegates the filtering to a Spring filter chain via a FilterChainProxy. Spring Security filter chain is by default represented by the DefaultSecurityFilterChain class.

fundamentals2

Reference:

org.springframework.security.web.FilterChainProxy org.springframework.security.web.DefaultSecurityFilterChain

Default Security Chain

Lets how the default security chain looks like. In the code below we have a basic Spring Security configuration with only the @EnableWebSecurity annotation.

@Configuration
@EnableWebSecurity
public class SecurityConfig {
}

The above configuration results in the following filter chain:

default chain 2

As you can see, by default Spring Security provides:

  • UsernamePasswordAuthenticationFilter - responsible for authenticating the user based on the username and password provided in the request.
  • BasicAuthenticationFilter - responsible for authenticating the user based on the basic authentication mechanism.
  • AuthorizationFilter - main class that is responsible for make a authorization decision for a given request.

The final decision about whether a request is allowed or denied is made by the AuthorizationFilter class, which is the last filter in the Spring Security filter chain. Authorization decision is made for a Authentication object, which is stored in the SecurityContextHolder class.

auth filter min 2

Reference:

org.springframework.security.web.access.intercept.AuthorizationFilter

Security Context Holder

SecurityContextHolder is a class which stores the details of the currently authenticated user, as the Spring Security documentation states:

The SecurityContextHolder is where Spring Security stores the details of who is authenticated. Spring Security does not care how the SecurityContextHolder is populated. If it contains a value, it is used as the currently authenticated user. The simplest way to indicate a user is authenticated is to set the SecurityContextHolder directly

Source: SecurityContextHolder

Reference: org.springframework.security.core.context.SecurityContextHolder

Setting Security Context

In order to grant access to a given resource in the AuthorizationFilter class, the SecurityContextHolder class must be populated with the Authentication object. For that reason, one of the security filters in the Spring Security filter chain must set the Authentication object in the SecurityContextHolder class.

set auth

AuthorizationFilter

Let’s have a better look at the AuthorizationFilter. It consumes the Authentication object from the SecurityContextHolder and delegates it to RequestMatcherDelegatingAuthorizationManager class, which produces the final authorization decision.

RequestMatcherDelegatingAuthorizationManager has a list of mappings that define which AuthorizationManager should be used for a given request.

RequestMatcherDelegatingAuthorizationManager default configuration has one mapping:

auth manager

auth manager code

Configuring Security Filter Chain

We can modify the default Spring Security filter chain by providing a custom configuration. To start with, we need to define a method that returns the SecurityFilterChain bean.

Let’s start with a simple configuration that returns not configured SecurityFilterChain bean:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http.build();
}
}

The code snippet above results in the following filter chain: no auth filter 5

What is worth noting is that the AuthorizationFilter is not present in the filter chain! This results in the fact that all requests are allowed by default.

Now, let’s add some basic configuration to restrict access to all requests:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth
.anyRequest()
.authenticated())
.build();
}
}

As you can see, one new filter has been added to the filter chain - AuthorizationFilter.

auth filter added 2

RequestMatcherDelegatingAuthorizationManager from the AuthorizationFilter has the same configuration as the one from the default Spring Security filter chain.

Now, if we try to request the application, we will get a 401 Unauthorized response:

curl -v localhost:8080
http 401 Unauthorized

Custom Security Filter

Now, let’s define some custom filter that will set the Authentication object in the SecurityContextHolder class.

add allow all filter 2

We will define a AllowAllFilter, which will set already authenticated Authentication object in the SecurityContextHolder class:

public class AllowAllFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication =
new TestingAuthenticationToken("username", "password");
authentication.setAuthenticated(true);
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
}
}

The password and username are not important in this case, as we already set the authenticated flag to true.

Now, let’s add the AllowAllFilter to the Spring Security filter chain. It’s important to remember that the order of the filters is important, so we need to define where the AllowAllFilter should be placed in the filter chain. In our case, we want to place it just before the AuthorizationFilter:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth
.anyRequest()
.authenticated())
.addFilterBefore(new AllowAllFilter(), AuthorizationFilter.class)
.build();
}
}

Once we debug the application, we can see out filer is set just before the AuthorizationFilter: allow all filter

Now, if we request the application, we will get a 200 OK response:

curl -v localhost:8080
http 200 OK

We can even log the security context in the MainController:

@RestController
public class MainController {
@GetMapping("/")
public void get() {
SecurityContext context = SecurityContextHolder.getContext();
System.out.println(context);
}
}

As you can see, the SecurityContext is populated with the Authentication object that we set in the AllowAllFilter:

SecurityContextImpl [
Authentication=TestingAuthenticationToken
[
Principal=username,
Credentials=[PROTECTED],
Authenticated=true,
Details=null,
Granted Authorities=[]
]
]

Tags

Share

Piotr Szarpak

Piotr Szarpak

Java passionate

Sed commodo, est quis maximus fermentum, massa ipsum euismod neque, in varius risus tellus quis lacus. Sed ac bibendum odio.

Expertise

Java > 11
DDD
Spring
Databases

Social Media

githubtwitterwebsite

Related Posts

OpenAI Chat Service With Spring AI
OpenAI Chat Service With Spring AI
January 31, 2024
2 min
© 2024, All Rights Reserved.
Powered By

Quick Links

Advertise with usAbout UsContact Us

Social Media