Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Spring Security is a framework for securing Spring-based applications. In this article, we will look over the core Spring Security concepts.
We have to secure our applications and data since security threats can not be ignored for an application. Spring Security is a framework for securing Spring-based applications. In this article, we will look over the core security concepts and how Spring Security provides solutions for the common security concerns.
Okay, let’s first figure out the problem in terms of security terminology.
Think that we have an asset; that’s a web application and it should be protected against threats.
Here, what a threat is, in information security, it’s a way of exploiting vulnerabilities; it’s a potential for the occurrence of a fatal impact which compromise of confidentiality, integrity or availability of our system. And if a threat is actualized, then we name it as an attack. A typical scenario is depicted in the following diagram.
Application Security Concepts
As you see in the diagram above, we want to protect the following aspects of our assets that are well-known as CIA Triad:
Confidentiality targets the privacy of our system. We have to ensure that our application is accessed only by authorized users. We call this concept authenticity in software development. And this leads us to another concept, identity. Our users have to be identified and authenticated. Besides, we have to make some kind of access controls to map which pieces of data to be accessed by which authenticated users. And this is authorization.
Integrity is about the consistency and accurate modification of our data, and this requires authorized modification of the data. The data have to be modified only by authorized users or processes. Besides, error detection and correction should be made.
Availability is about the continuity of the authorized access to the system. Our assets should be accessible in a reasonable time, and with a capacity enough, our services have to response in an acceptable period of time. If not, we call it denial of service, it’s the opposite of availability.
As you see, authentication and authorization is at the heart of the security. Spring Security provides several configurable servlet filters to provide authentication and authorization for our web applications and also for supplying these aspects, it provides the commons in the presentation layer such as login pages.
To understand the Spring Security, we should first delve into the basics up to the Servlets. In Java EE world, Servlet simply is a web technology with which we can receive and handle the HTTP requests, create HTTP response and return that response back to the client.
Servlet technology makes a basis for other Java web technologies on top of which other frameworks put their extensions. For example JSP, JSF, Spring MVC are such technologies which are uses Servlet technology at behind.
***So we can say that, Java + Http means Servlet in its core.
When we get to a Spring web application from that point, we can now say that it should be a Servlet somewhere managing the http requests. And that is the DispatcherServlet. Let’s look over the following diagram:
Dispatcher ServletThe DispatcherServlet, as a front controller, gets the http request and forwards it to the Spring components that can be a Controller or an Actuator Endpoint.
While doing this, some security aspects could be handled by leveraging another Servlet feature, filters.
Filters are heavily used in Servlet technology as pre-processors and/or post-processors and can be chained.
So, in our case, Spring Security configures some security filters on the DispatcherServlet on behalf of us. When a request comes, these filters run and filter the request by making some checks such as CSRF exploits or/and authenticates the request. And after the run of these filters, the DispatcherServlet routes the request to our @Controllers/@RestControllers/@Endpoints.
That’s the outline of the Spring Security, and it’s time to inspect the details.
Spring Security is mostly about wiring some security filters to the DispatcherServlet and making the configuration of these filters. And as you guess, Spring Boot makes this wiring process easy for us with auto-configuration.
Now, let’s try it out in a simple Spring Boot application and see the default behaviours of an auto-configured security by Spring Boot.
Adding security to a Spring Boot application is just one step away from us. We just add the following dependency and see what happens then.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
If we start up the application after adding this dependency, the default security configuration is applied with the use of SecurityAutoConfiguration class. And later on, if we make a form-login request then we will first be directed to a login page and after the authentication with the password printed in the Spring Boot logs, we will be able to access the requested page. Or, if we make a request to any of our rest endpoints we will be authorized automatically with the password printed in the Spring Boot logs and we will be able to access the endpoint.
And all these are happened via the auto-configured security filters over the DispatcherServlet.
As we already know, we can override the auto-configured properties in our application configuration. And here, we can do this to specify our application’s credentials with the following properties:
spring.security.user.name spring.security.user.password
Now, we know that Spring Security makes some wirings with servlet filters for us at behind and that filters secure our applications.
We can print all that security filters configured for us by adding the annotation below:
@EnableWebSecurity(debug = true)
And let’s look over the default security filters in detail.
Security Filter ChainHere, we will cover some of these security filters mostly used.
As you see in the diagram above, Spring provides a special filter named DelegatingFilterProxy which acts as a central filter wiring the Servlet container to the Spring Application Context. This special filter delegates all the work to another special filter named FilterChainProxy provided by Spring Security. Why such a configuration is designed is that; while DelegatingFilterProxy is wired as a standard configuration of Servlet Container, the filters provided by the Spring Security are loaded lazily by the Spring framework. When FilterChainProxy gets the request, it makes an evaluation over the request and routes it to one of the filter-chains configured via the Spring Security named SecurityFilterChain. And later on, the security filters in this chain are executed sequentially and in the order as declared in FilterComparator. That’s it.
As we already know, in a servlet container, URL is the only decision parameter for selecting the filters to be executed. However, SecurityFilterChain has a RequestMatcher that can have a specific matching algorithm on HttpServletRequest and that matcher is checked by the FilterChainProxy to find the correct security filter chain.
SecurityContextPersistenceFilter is the gateway filter to the authentication mechanism in Spring Security. Basically, it manages the lifecycle of the SecurityContext in a request by getting the SecurityContext probably from HttpSession and associating it with the current execution thread in the SecurityContextHolder and at the response time it simply clears the SecurityContext from the SecurityContextHolder.
Yes, that’s enough to know this much for now. We will deeply cover the authentication process later and we will go over all these concepts.
HeaderWriterFilter is responsible for writing some security headers to the current response to enable browser protection.
Some of the headers inserted by Spring Boot are as follows:
.headers() .contentSecurityPolicy("script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/");
.headers() .referrerPolicy(ReferrerPolicy.SAME_ORIGIN);
.headers() .featurePolicy("geolocation 'self'");
CsrfFilter is about to prevent the Cross-Site Request Forgery attacks. This filter is executed for any request that allows state to change, so in your API the REST semantics have to be strictly applied and you should not change state with the HTTP methods GET, HEAD, TRACE, OPTIONS.
LogoutFilter is executed if the incoming request matches with the RequestMatcher of this filter which is defaulted to /logout. This filter has some default handlers to be executed while logout and you can also write your own custom logout handlers for specific use-cases.
We will deeply cover the logout process, so here that’s enough to know that there is a such filter.
The RequestMatcher of this filter is defaulted to /login. And if a /login request is made then this filter reads username and password from the request body, if exists then authenticates the user.
BasicAuthenticationFilter is executed if Basic Authentication data is provided in the header of the HTTP request. Meaning that, if a value is found for the key Authorization in the header, then the filter tries to authenticate the user.
This filter helps to do a very familiar action. When we make a request for a resource that requires authentication, the authentication process starts and we provide our credentials and login. After the authentication process, the request before the authentication process is got from the RequestCache and reconstituted.
SecurityContextHolderAwareRequestFilter wraps the original HttpRequest with SecurityContextHolderAwareRequestWrapper. By doing so, the authentication-related methods provided by the HttpServletRequest, such as getRemoteUser, isUserInRole.., can access the Authentication object in the SecurityContext and provide the related data.
SpringSecurity creates an Authentication object for the authorized requests. For example, if you login with formLogin or basic authenication then a UsernamePasswordAuthenticationToken exists in the SecurityContext as Authentication object. However, an unauthorized user can acces unprivileged resources without authentication. This time no Authentication object exists in the SecurityContext. An unauthorized user can access unprivileged resources with one of two ways:
Calling anonymous, guarantees the SecurityContext having an Authentication object having the role ANONYMOUS. But technically this is not different than having no Authentication object.
These are some of the filters executed within an http request. We will cover these and some of other filters in the later topics.
As we have already noted that adding spring-boot-security-starter dependency makes use of default security configuration for us with the use of SecurityAutoConfiguration.
So if we want to disable security completely we can extract these dependency from the pom.xml or we can exclude the SecurityAutoConfiguration as following:
@SpringBootApplication(exclude = { SecurityAutoConfiguration.class })
But in real life scenarios, we generally do not do this, instead, we need to override the default configuration to be able to handle more advanced security requirements.
For basic scenarios, spring security auto-configuration provides a quick solution, but mostly we need a more customized security configuration, such as separating the roles of the api provided, different authentication schemes and etc.
Custom Configuration can be applied implementing separate configuration classes by extending WebSecurityConfigurerAdapter.
Now let’s look over the overall process and try to understand the custom configuration. We will use the following diagram to understand it better.
Filter Chain Configuration
Here is a sample of the custom configuration of a SecurityFilterChain:
@Configuration @Order(value = 101) public class BasicAuthSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.headers().contentSecurityPolicy("script-src 'self'"); http.csrf().disable(); http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.antMatcher("/api/**") .authorizeRequests() .antMatchers("/api/order/**").hasRole("USER") .anyRequest().authenticated() .and().httpBasic(); } @Override public void configure(WebSecurity web) throws Exception { web.ignoring() .antMatchers("/register", "/api/public/**"); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); auth.inMemoryAuthentication() .withUser("test_usr").password(encoder.encode("test_passw")).roles("USER") .and() .withUser("admin_usr").password(encoder.encode("admin_passw")).roles("USER", "ADMIN"); } }
WebSecurityConfigurerAdapter provides 3 overloaded methods to configure the web security:
In this section, we will look over the third one and inspect how the HttpSecurity is used to configure security filter bindings.
HttpSecurity allows configuring web based security for specific http requests. This is where the security filters we have listed above are configured for certain url paths. Let’s look over the methods provided by HttpSecurity.
First of all, the order of method calls on the HttpSecurity matters and the common usage is as follows:
As we have noted earlier, a WebSecurityConfigurerAdapter is the configuration of a SecurityFilterChain that we have inspected with the diagrams above. A SecurityFilterChain is targeted if and only if its RequestMatcher is evaluated and matched with the incoming request url. antMatcher provides a single url matching option for the SecurityFilterChain. Meaning that, if a single url pattern is enough to map the requests for the current SecurityFilterChain then antMatcher is an appropriate option to use.
@Configuration public class BasicAuthSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.antMatcher("/api/**") .authorizeRequests() .antMatchers("/api/order/**").hasRole("USER") .anyRequest().authenticated() .and().httpBasic(); } }
In the code above, the SecurityFilterChain is matched only for the requests starting with /api and so the antMatcher is an option to match the requests.
If we need to map multiple urls to the SecurityFilterChain then we can combine the url paths using requestMatchers and antMatchers.
@Configuration public class MultipleUrlConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.requestMatchers() .antMatchers(HttpMethod.GET, "/web/**") .antMatchers(HttpMethod.POST, "/web/**") .and().authorizeRequests() .anyRequest().authenticated() .and().formLogin(); } }
This is basically how HttpSecurity matches the urls and maps the requests to the relevant SecurityFilterChains. We will have a lot to go with HttpSecurity in the next articles since it provides a wide range of configuration methods.
In this article, we have looked over the Spring Security basics. First, we defined the security aspects and threats, then we introduced the Spring Security architecture based on the Servlet technology. We have started up a basic Spring Boot application with auto-configured Spring Security. Later on, we have made some custom configuration and looked over the RequestMatcher pattern.
We have a lot to go on Spring Security. In the next article, we will examine the authentication and authorization process in Spring Security in detail.
Keep following for the next article.
You can see the sample code for this article on my Github page:
https://github.com/erolhira/spring/tree/master/spring-security