Spring Security in Motion — Part 1
In today’s interconnected world, developing secure apps has become a primordial concern for programmers and organisations. Unsafe coding practices result in costly vulnerabilities in application software that can lead to drastic damages.
This article cover how to integrate spring security in your application from initialisation to custom configurations.
1. Servlet Filter
Servlet is a Java program running on a Web server or an application server that process and respond to client requests. Servlet container holds servlet objects.
A Servlet filter is an object that is invoked at the pre-processing and/or post-processing of an incoming request. It is mainly used to perform filtering tasks such as encoding, encryption, decryption, compression ect.
Usually we define multiple filters, and to facilitate the delegation, a filter chain comes on the top and would be responsible to forward responses to next filters.
ApplicationFilterChain is the implementation of FilterChain used to manage the execution of a set of filters for a particular request. When filters has all been performed, next call to doFilter() will delegate to the servlet’s service() method in order to dispatch to appropriate handler.
Spring security performs most of its core logic using servlet filters. The DelegatingFilterProxy is a filter which works as a bridge between Servlet container’s life-cycle and Spring’s Application Context. This filter delegates the execution of processing to FilterChainProxy.
This filter contains all the details about the different security filters. I’ts is configured using a list of SecurityFilterChain instances, each of which contains a RequestMatcher and a list of security filters which should be applied to matching requests. Most applications will only contain a single filter chain. Note here that A filter chain contains servlet filters, in contrast SecurityFilterChain contains security filters, just to avoid confusion.
The first match for a given request will be used to define all of the security filters that apply to that request. This means you must put most specific matches at the top of the list, and ensure all Filters that should apply for a given matcher are entered against the respective entry.
We can have multiple SecurityFilterChains configured in our application. Spring security provides several securities filters that you can define in your chain.
The FilterChainProxy determines which SecurityFilterChain should be used for an incoming request, There are several benefits of this architecture for example it keeps your code flow centralized and there are no multiple entry points to the security stack.
SecurityFilterChain interface has two methods, match and getFilters(). The former is invoked to assess if a request match certain ant pattern, the later returns a List of filters that the request would go through if the url pattern matches.
DefaultSecurityFilterChain is the actual standard implementation of SecurityFilterChain.
In this section we discussed DelegatingFilterProxy, a special servlet filter that delegates to FilterChainProxy, a manager for multiple SecurityFilterChains with each SecurityFilterChain containing one or mutliple security filters.
2. Authentication and Authorisation in Spring Security
In this section, we introduce how authentication and authorisation is performed by spring security. We first present some key components, then, we delve into the process of verification.
One of the features of Spring is that, through scanning, it creates and manages the lifecycle of our beans.
In Spring Security, to facilitate configurations, several objects are created and completed dynamically at runtime, but wait, does that means those objects are not maintained by Spring ? What if I still need some spring post processors like @PostConstruct ?
To deal with those kind of situations, Spring Security introduced ObjectPostProcessor to enable new objects to be springable. Thus, annotations such as @Autowired and @PostConstruct can be processed.
One implementation is AutowireBeanFactoryObjectPostProcessor.
If you look at the postProcess method, it actually uses the AutowireCapableBeanFactory to Initialise the given raw bean, applying factory callbacks, also applying all bean post processors and perform auto wiring, that is, populate the given bean instance through applying after-instantiation callbacks and bean property post-processing.
Authentication is a object that carries the authentication information of the user, it contains:
- Principal : identification of the user.
- Credentials : represents authentication credentials such as a password.
- Authorities : represents an authority granted to the user such as roles and scopes.
When the authentication process has been completed successfully, it will be stored in the SecurityContextHolder.
Registers security information associated with the current request (user). Note that to deal with multithreading, the Security Context is stored in a SecurityContextHolder and each resulting authentication object is associated with a given session, thus, each time we need to pull the authentication to populate the SecurityContextHolder we would just tap into the http session and thus avoiding to reauthenticate the user at each request. This process is performed by the SecurityContextPersistenceFilter that we will discuss later.
SecurityContextHolder associates a given SecurityContext with the current execution thread.
AuthenticationManager is responsible for verifying the encapsulated user information.
Multiple AuthenticationProvider can be injected into AuthenticationManager (ProviderManager). Each AuthenticationProvider performs a specific type of authentication. For example,
DaoAuthenticationProvider supports username/password based authentication while
JwtAuthenticationProvider supports authenticating a JWT token.
The default implementation of AuthenticationManager is ProviderManager, which delegates a set of AuthenticationProvider instances to implement authentication.
Each AuthenticationProvider has an opportunity to indicate that authentication should be successful, fail, or indicate it cannot make a decision and allow a downstream AuthenticationProvider to decide. If any AuthenticationProvider passes, the authentication is considered as successful.
To sum up, authentication process consists of wrapping user information into an authentication object, delegate to ProviderManager, then, invoke sequentially AuthenticationProviders authenticate method, if turns out that a provider authenticated successfully the user, A list of GrantedAuthority objects is stored in the SecurityContext. These represent the authorities that have been granted to the principal, it would be later read by AccessDecisionManager when making authorization decisions.
This is a perfect bridge to Authorisation section.
Once the authentication is successful, we can proceed with authorization, which is achieved through AccessDecisionManager. There are three implementations. The default is AffirmativeBased. Decisions are made through AccessDecisionVoters, which is a similar to ProviderManager entrusting AuthenticationProviders to authenticate.
GrantedAuthority represents an authority granted to an authentication object, that is, to the current user. Usually the authority is prefixed string such as ROLE_ADMIN, so that when you invoke something like hasRole (“ADMIN”), it will check if the user has an authority string ROLE_ADMIN in its authorities collection. But that doesn’t not mean you must always prefix your authority, you may have already seen something like hasAuthority(‘readAndWrite’).
AccessDecisionManager — AccessDecisionVoter
AccessDecisionVoters are a set of voter responsible for voting on authorization decisions. AccessDecisionManager is responsible for making final access control decisions.
The AccessDecisionVoter class
AccessDecisionVoter interface has three methods:
The ConfigAttribute is the condition to be met to access the object (defined when security filter chain has been configured, more on that later). Its core is to extract GrantedAuthority from authentication and compare it with ConfigAttribute to see if the conditions are met, that’s it.
The AccessDecisionManager class
AccessDecisionManager interface contains three methods. The object is the resource that the user wants to access.
Voting Decision approach
As stated earlier, AccessDecisionManager maintains a list of voters, based on their responses a final decision is made. There are three decision logic, by default, AffirmativeBased is used.
In this approach, as long as any AccessDecisionVoter returns an positive response, access is granted.
In this approach, a final decision is made based on the majority authorization access decisions.
This is the hard approach, every AccessDecisionVoter should grant permission otherwise an exception is thrown.
Note that you can also use your own approach for access control decision.
So far, we discussed brielfy how authentication and authorisation is performed, let’s explore how to configure it in our Spring application.
3. Spring Security Objects
Before delving into more details of spring security architecture, we need to understand how spring security objects are built. There three kinks of Spring Security Object; HttpSecurity, AuthenticationManager and WebSecurity. The building process of each object will be discussed in this section.
The SecurityBuilder interface
SecurityBuilder is an Interface for building an Object.
The AbstractSecurityBuilder class
A implementation of SecurityBuilder class that ensures that the built object is only built once (through doBuild).
The AbstractConfiguredSecurityBuilder class
This class implements AbstractSecurityBuilder, it maintains the build lifecycle such as initialising all registered SecurityConfigurers. Keep in mind that each Security Filter in a chain are configured via a SecurityConfigurer, they would be appended via AbstractConfiguredSecurityBuilder#add.
AbstractConfiguredSecurityBuilder has two apply() methods used to apply SecurityConfigurers to a Security Builder.
SecurityConfigurers gets effective through init() and configure() methods invoked from the doBuild call. When configure gets invoked it will go through all configurers and invoke configure method.
As shown below the building procesess is divided into four phases.
There are three implementations of AbstractConfiguredSecurityBuilder class, WebSecurity, HttpSecurity and AuthenticationManagerBuilder, those are essence components of spring security.
The core purpose of WebSecurity is to generate a Security FilterChainProxy (the filter that would contains all user-defined configurations)
FilterChainProxy (our famous servlet filter) is built via performBuild.
HttpSecurity allows configuring web based security for specific http requests. This is where the security filters are configured for specified url paths.
Before going through the implementation, let’s discuss some basic filters.
Core Security Filters
Spring defines many security filters to process http requests, in this section, we will go though some of them, for more details, have a look at https://docs.spring.io/spring-security/site/docs/3.0.x/reference/security-filter-chain.html.
CsrfFilter: Protect against CSRF attack using a synchronizer token pattern.
SecurityContextPersistenceFilter: populates the SecurityContextHolder. When a request comes, it will take the SecurityContext out of the HttpSession and put it in the SecurityContextHolder. After all interceptors are processed, the SecurityContext is stored in the HttpSession and the reference in the SecurityContextHolder is cleared.
BasicAuthenticationFilter: responsible for processing basic authentication credentials presented in HTTP headers.
FilterSecurityInterceptor: to protect web URIs and raise exceptions when access is denied.
The HttpSecurityBuilder interface
It’s an interface mainly used to build an HttpSecurity instance.
The HttpSecurity class
HttpSecurity allows configuring web based security for specific http requests.
The SecurityFilterChain (DefaultSecurityFilterChain) is built via performBuild.
Interface for operating on a SecurityBuilder that creates a ProviderManager (AuthenticationManager).
SecurityBuilder used to create an AuthenticationManager. Allows for easily building in memory authentication, LDAP authentication, JDBC based authentication, adding UserDetailsService, and adding AuthenticationProvider.
It contains a List of authenticationProviders used for authentication process. After the user is authenticated, we can specify using eraseCredentials whether the password needs to be deleted in the Authentication object.
Let’s look at the performBuild() method that is really used to create the AuthenticationManager.
The provider would be set during the configure phase via AbstractDaoAuthenticationConfigurer(see next ).
In this section we explored how ProviderManager, HttpSecurity and WebSecurity building processs. In the next section, we will explore how do we operate on security builders, in particular AuthenticationManagerBuilder, in order to configure it according to user needs.
Allows for configuring a SecurityBuilder. All SecurityConfigurer first have their init method invoked. After all init methods have been invoked, each configure method is invoked.
SecurityConfigurerAdapter provides a mechanism for setting security properties via security configurers. It also provides access to the builder (for example AuthenticationManagerBuilder ) through getBuilder().
4.1 AuthenticationManager Configurers
The above diagram depicts the relationship between DaoAuthenticationConfigurer, InMemoryUserDetailsManagerConfigurer and JdbcUserDetailsManagerConfigurer used to configure the AuthenticationManagerBuilder discussed earlier. We will discuss them one by one on the following sections.
The UserDetailsAwareConfigurer class
in this inheritance system, UserDetailsAwareConfigurer extends SecurityConfigurerAdapter and adds a method to obtain UserDetailsService instance.
The UserDetailsService interface
UserDetailsService is the core interface for loading user-specific data. It is used as a user DAO throughout the framework and is the strategy used by DaoAuthenticationProvider. The interface requires only a read-only method, which simplifies support for new data access strategies.
The UserDetails interface
UserDetails is an interface that provides core user information. For security purposes, Spring Security does not use the implementation directly.
The AbstractDaoAuthenticationConfigurer class
Provides the implementation of getUserDetailsService() and configure() method in order to set the authentication provider for the passed builder.
An AuthenticationProvider implementation that retrieves user details from a UserDetailsService.
The UserDetailsServiceConfigurer class
An initUserDetailsService() method was added before the configure() method was executed, allowing subclasses to initialize UserDetailsService. For example, it may add users, initialize the architecture, etc.
The UserDetailsManagerConfigurer class
The constructor receives a UserDetailsManager, which is an extension of UserDetailsService and provides the ability to create new users and update existing ones. In the initUserDetailsService() method, the UserDetails or UserBuilder received via withUser() method will be used to initially create some users through the UserDetailsManager.
The InMemoryUserDetailsManagerConfigurer class
Use InMemoryUserDetailsManager to complete operations such as user creation and update in memory. Users informations are stored in a map. This is not intended to be used in a production system.
An extension of the UserDetailsService which provides the ability to create new users and update existing ones.
The InMemoryUserDetailsManager class
InMemoryUserDetailsManager implements UserDetailsManager by storing users information in a map. Mainly intended for testing and demonstration purposes, where a full blown persistent system isn’t required.
The JdbcUserDetailsManagerConfigurer class
Configure AuthenticationManagerBuilder for JDBC authentication. It also allows users to be easily added to the database used for authentication and setting up the schema. The only required method is dataSource (DataSource), all other methods have reasonable default values
The DaoAuthenticationConfigurer class
As discussed earlier, UserDetailsServiceConfigurer provide subclasses the ability to initialize a UserDetailsService (Jdbc or InMemory), DaoAuthenticationConfigurer on the other hand can be used to provide custom UserDetailsService and allow configuring a DaoAuthenticationProvider. During initialisation userDetailsService is provided directly (so initialisation of the service should have already been performed).
So far we discussed how to configure a user authentication mechanism by acting on the authentication builder via a userDetails Service that could either be inMemory or using a jdbc connector or by providing a custom user details service. So That’s it for the Authentication, we know how to build and configure an Authentication manager.
4.2 Http Security Configurers
In the previous section we explored how to configure an authentication manager builder. The following section discuss how to configure an http security builder, that is adding our specific filters.
This class is a base class for multiple configurers associated with HttpSecurity, such as HttpBasicConfigurer, FormLoginConfigurer and ExpressionUrlAuthorizationConfigurer.
As shown below it adds a basic authentication filter using the authentication manager being build and configured.
It gets pushed when using httpBasic.
It adds the filterSecurityInterceptor for authorization handling ect.. The configure method of this object is in its parent class AbstractInterceptUrlConfigurer.
its added when authorize requests is invoked.
Add a filter for the username password authentication taken out of the body of the request.
The UsernamePasswordFilter is appended below when invoking configure on http security object.
Let’s do again our summary of this section, what have we learnt: we explored how to use our authentication manager (assuming that it was already built) in order to build and configure http security object that would initialise our securiyFilterchain with defined security filters such as FilterInterceptor.
A small note is that the authenticationManager is build just before the configuration of the HttpSecurity object as shown below. It would be set available for other filters to use it.
It would use the builder that has been injected while creating the HttpSecurity instance.
So what’s next ? We still have one security component to investigate that would actually wrap the http security object (that wraps the authentication manager builder) in order to build our final servelt filter.
4.3 WebSecurity Configurers
The WebSecurityConfigurer interface
WebSecurityConfigurer is designed to configure a build target as Filter.
WebSecurityConfiguration - @EnableWebSecurity annotation
Every project configuration has a starting point, in spring security, @EnableWebSecurity annotation is used to initialise the whole configuration.
This annotation class imports two configuration classes (HttpSecurityConfiguration and WebSecurityConfiguration) and two ImportSelectors (Spring WebMvcImportSelector, OAuth2ImportSelector). The annotation @Configuration is crucial for spring to process these configurations.
In this article we will mainly focus on WebSecurityConfiguration, which is the core of Spring Security.
WebSecurityConfiguration is responsible for creating a FilterChainProxy using a WebSecurity instance.
The class contains two crucial methods, setFilterChainProxySecurityConfigurer() and springSecurityFilterChain().
During spring initialisation process, @autowired annotations are processed before @bean. Thus, setFilterChainProxySecurityConfigurer is first executed.
The above code snippet will first resolve all the webSecurityConfigurer implemented classes (including WebSecurityConfigurerAdapter) that were injected in the spring container via spring scanning. This is done by delegating to AutowiredWebSecurityConfigurersIgnoreParents#getWebSecurityConfigurers() through the @Value annotation.
Next, spring security filter chain is built via springSecurityFilterChain() method.
As shown above, it consists of creating a WebSecurityConfigurerAdapter (if user has not defined any but usually you define yours) and start the web security build lifecycle discussed earlier.
The WebSecurityConfigurerAdapter class
WebSecurityConfigurerAdapter is a very core class, used to extend user-defined Security Chain rules It’s init method shown below would be invoked later when building process starts.
One crucial step to keep in mind for later is that when we create our http security instance we add it via addSecurityFilterChainBuilder in order to hold its building process for later.
The init method call getHttp() to get an http instance, and assign the retrieved instance to WebSecurity’s securityFilterChainBuilders property via the web.addSecurityFilterChainBuilder method. This property will be used when we execute the build. The second is to add a postBuildAction to WebSecurity. After the build is complete, we will take the FilterSecurityInterceptor object from http and assign it to WebSecurity.
HttpSecurity building process
getHttp method will add various SecurityConfigurer and will eventually configure a variety of security filters (see SecurityConfigurers section).
First we set the parent authentication manager. If the user has not specified any configuration for the AuthenticationManager, that is, by overriding the below method
It would delegate to AuthenticationConfiguration, otherwise it would delegates to local builder which is set during setApplicationContext.
Next, HttpSecurity object is instantiated, and WebSecurityConfig.configure(HttpSecurity) method is invoked to load the user defined HttpSecurity configuration. By default the configuration is as follow.
If you need to override certain requests, you need to override the method below.
So now we have our webSecurity instantiated (that would build our target chain proxy) and contains HttpSecurity object inside the list of securityFilterChainBuilders (see WebSecurity performBuild in earlier sections that actually trigger the build of these securityFilterChains) collection that would build our security filter chain.
Notice that the filter is actually a DelegatingFilterProxy, and not the class that will actually implement the logic of the filter. What DelegatingFilterProxy does is delegate the Filters methods through to a bean which is obtained from the Spring application context. This enables the bean to benefit from the Spring web application context lifecycle support and configuration flexibility. The bean must implement Filter interface and it must have the same name as that in the filter-name element. Read the Javadoc for DelegatingFilterProxy for more information
what is interesting for us is the init method invoked by WebSecurityConfiguration to create the DelegatingFilterProxy known as the Spring Security Filter Chain (springSecurityFilterChain). The springSecurityFilterChain is the Filter that the DelegatingFilterProxy delegates to.
DelegatingFilterProxy actually will just act as a delegator by searching for a bean with name springSecurityFilterChain from the spring container. you can find below a snippet code that actually does delegation part.
you can find below how delegate was initialized.
The snippet code below gets FilterChainProxy from the spring container (web application context), Next some initialisation are performed if necessary then the filter is returned.
Let’s discuss now our famous servlet filter FilterChainProxy.
The FilterChainProxy class
We will not go into each tiny details but we will discuss relevant points and further details can be easily explored at this point.
When a request comes in, it enters FilterChainProxy to execute the doFilter method, it will go through all the filters and ApplicationFilterChain sees that we fell off the end of the chain — it will call the servlet instance through service() method for actual processing ( see ApplicationFilterChain source code).
Get all filters first, next, delegates to VirtualFilterChain.
VirtualFilterChain is Internal FilterChain implementation used to pass a request through the additional internal list of filters which match the request.
So far we reached the point where the springSecurityFilterChain bean is inside our spring container.
In order to use our servlet security filter springSecurityFilterChain, one possibility is to configure it using the traditional XML-based approach . Basically, DelegatingFilterProxy will look for a Spring bean by the name of filter (
<filter-name>) and delegates all works to that bean.
or using WebApplicationInitializer
On Startup it will register the springSecurityFilterChain.
For more details have a look here https://www.baeldung.com/spring-security-login.
We reached the end of this part. There are a lot of things to grasp in one shot, you may find useful the following diagram, hope it helps.
To sum up, incoming http requests are routed to the DispatcherServlet by the servlet container. Before reaching the ressource, it will go first through a series of servlet filters, when it reaches the DelegatingFilterProxy filter, it would delegates to FilterChainProxy which is responsible to routing that request to an appropriate SecurityFilterChain. The first SecurityFilterChain that matches the request would apply the inner security filters. These filters are configured via a configuration class which implements WebSecurityConfigurerAdapter. In turn, each security configurator would act on its builder during the buidl lifecycle.
The next part would be dedicated to Spring Security OAuth2.
References and Further reading:
If you are ready to start securing an application see the Getting Started sections for servlet and reactive. These…
Spring Securityのお話 - シスアーキ in はてな
ハーイ！僕です。最近、Spring Securityと戦っています。今のプロジェクトでセキュリティまわりを僕が担当しているのでね。 Spring Frameworkを勉強しはじめて、最初につまづいたのがSpring ...
[JAVA] Spring Security usage memo Basic / mechanism
Authentication / Authorization Story Remember-Me story CSRF story Session management story The story of the response…