An architectural overview of SpringSecurity

An introduction to Spring Security

Spring Security provides full support for authentication, authorization, and common vulnerability protection. The version used is Spring Security 5.5.2.

Concept definition

  • Authentication: Authentication authenticates users who attempt to access resources.

    • The authentication scenario is the login process. The user name and password are commonly required. When the authentication succeeds, the user can be authorized.
  • Authority: Allows users to set permissions on resources. Only users with corresponding permissions can access resources.

The technology principle

In servlet-based applications, Spring Security uses the Filter mechanism to implement authentication, authorization, vulnerability defense and other functions.

Simply put, you can set Filters to a Servlet that form a FilterChain.

Request interception processing

Each time a request comes in, it is first captured by the Filters in the FilterChain, and each Filter can do some pre-processing of the request or some post-processing of the response before reaching the Servlet. The specific process is shown in the figure below:

FilterChain instance object

The Filter in FilterChain has two functions:

  • Modify HttpServletRequest or HttpServletResponse so that subsequent Filters or servlets in the FilterChain get the modified request and response content.

  • The Filter can intercept the request and respond by itself, breaking the FilterChain and preventing subsequent Filters and servlets from receiving the request.

DelegatingFilterProxy
  • Based on the Servlet specification, we can inject some custom Filters into the Servlet container, but in the Spring application, beans implementing the Filter interface are not aware by the Servlet container. The ServletContext#addFilter method was not called to register with the FilterChain.

  • Spring provides a DelegatingFilterProxy proxy class, DelegatingFilterProxy implements Filter, so it can be injected into the FilterChain, and when the request comes in, It forwards the request to the Bean entity in the Spring container that implements the Filter interface, so DelegatingFilterProxy Bridges the Servlet container and the Spring container.

The action diagram of DelegatingFilterProxy is shown below:

When the request comes in, DelegatingFilterProxy gets the FilterBean entity from ApplicationContext and forwards the request to it, with pseudocode like the following:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
    // Lazily get Filter that was registered as a Spring Bean
    // For the example in DelegatingFilterProxy delegate is an instance of Bean Filter0
    Filter delegate = getFilterBean(someBeanName);
    // delegate work to the Spring Bean
    delegate.doFilter(request, response);
}
Copy the code

FilterChainProxy

The Filter Bean entity in the Spring container can be injected into the FilterChain function in the Servlet container. Based on this, Spring Security provides a FilterChainProxy Bean entity to the Spring container. The FilterChainProxy implements the Filter interface, so the request is captured by FilterChainProxy and Spring Security is ready to work.

By default, DelegatingFilterProxy gets the FilterChainProxy entity from the Spring container, and FilterChainProxy is also a proxy class, It will eventually forward the request to the SecurityFilterChain provided by Spring Security, as shown below:

Note: FilterChainProxy is the actual entry point for Spring Security. To trace the entire Spring Security call process, set the breakpoint at FilterChainProxy#doFilter when you are debugging the code.

SecurityFilterChain

The SecurityFilterChain, like the Servlet FilterChain, maintains many Filters provided by Spring Security. Each SecurityFilter has a different function. Such as login authentication, CSRF defense… As shown below:

In addition, Spring Security supports multiple SecurityFilterchains, each of which is responsible for different requests (e.g. by request address), so that different authentication rules can be set for different requests. The source code is as follows:

public interface SecurityFilterChain {
    // Match the request
    boolean matches(HttpServletRequest request);
    // Gets all the filters in the SecurityFilterChain
    List<Filter> getFilters(a);
}
Copy the code

Specifically, when a request arrives at a FilterChainProxy, it internally gets the corresponding SecurityFilterChain based on the current request, and then forwards the request to all the Security Filters in that SecurityFilterChain. As shown below:

Security Filters

Spring Security ultimately handles the request by a Security Filters in a SecurityFilterChain that is set to Bean injection into the Spring container. And the order of these Filters is important. Here is the complete sequential list of Security Filters built into Spring Security:

  • ChannelProcessingFilter: Ensures that the request is delivered to the requested channel. The most common usage scenario is to specify which requests must use HTTPS, which requests must use HTTP, and which requests can use either protocol.

  • WebAsyncManagerIntegrationFilter: integrated SecurityContext WebAsyncManager in asynchronous request to the Spring Web mechanism.

    • Note: a SecurityContext is a SecurityContext that is used to store user authentication information.
  • SecurityContextPersistenceFilter: Before each request processing, from the Session (default HttpSessionSecurityContextRepository) gets SecurityContext, then set it to SecurityContextHolder; At the end of the request, the SecurityContext stored in the SecurityContextHolder is re-saved into the Session and the SecurityContext in the SecurityContextHolder is cleared.

    • SecurityContextPersistenceFilter can HttpSecurity# securityContext () and related methods to introduce its configuration object SecurityContextConfigurer configuration.
  • HeaderWriterFilter: This filter can add some response headers to the response, such as X-frame-options, X-xss-protection and x-Content-type-options, to enable browser Protection.

  • HeaderWriterFilter can be customized with HttpSecurity#headers().

  • CorsFilter: Handles cross-domain resource sharing (CORS).

    • CorsFilter can be customized with HttpSecurity#cors().
  • CsrfFilter: Handles cross-site request forgery (CSRF).

    • CsrfFilter can be turned on or off with HttpSecurity# CSRF (). There is no need to use CSRF in a back-end separation project.
  • LogoutFilter: processes the logout request.

    • LogoutFilter can customize the exit logic with HttpSecurity#logout().
  • OAuth2AuthorizationRequestRedirectFilter: used to build the 2.0 certification request, the user is redirected to the authentication request interface.

    • Note: Related modules such as Spring-security-oAuth2 need to be added to this filter.
  • Saml2WebSsoAuthenticationRequestFilter: based on SAML SSO single sign-on authentication request filter.

    • Note: Saml2WebSsoAuthenticationRequestFilter need to add the Spring Security SAML module.
  • X509AuthenticationFilter: X509 authentication filter.

    • X509AuthenticationFilter can be enabled and configured with SecurityContext#X509().
  • AbstractPreAuthenticatedProcessingFilter: request filter base class certification pretreatment, the certification body has been authenticated by the external system. The goal is only to extract the necessary information on the subject from the incoming request, not to authenticate them. Can inherit this class to the implementation and through HttpSecurity# addFilter method to add a personalized AbstractPreAuthenticatedProcessingFilter.

  • CasAuthenticationFilter: Used to process CAS sso authentication.

    • Note: CasAuthenticationFilter requires the Spring Security CAS module to be added.
  • OAuth2LoginAuthenticationFilter: OAuth2.0 login authentication filter.

    • Note: OAuth2LoginAuthenticationFilter need to add the spring ws-security – oauth2 related modules.
  • Saml2WebSsoAuthenticationFilter: based on SAML SSO single sign-on authentication filter.

    • Note: Saml2WebSsoAuthenticationFilter need to add the Spring Security SAML module.
  • Login UsernamePasswordAuthenticationFilter: used for handling forms authentication. The default processing interface is /login. The form must provide two parameters: username and password. The default parameter name (key) is username and password, which can be changed using the usernameParameter and passwordParameter methods.

UsernamePasswordAuthenticationFilter can HttpSecurity# formLogin () and related methods to introduce its configuration object FormLoginConfigurer configuration.

  • OpenIDAuthenticationFilter: based on the OpenID authentication protocol of authentication filter.

  • DefaultLoginPageGeneratingFilter: if there is no configuration to the login page, then it will be the default use the filter to generate a login form page.

    • Note: The default login page interface is /login
  • DefaultLogoutPageGeneratingFilter: generate default from the login page.

    • Note: The default interface to exit the login page is /logout
  • ConcurrentSessionFilter: Used to check whether a Session expires and update the latest access time. The filter may be executed multiple times.

  • DigestAuthenticationFilter: used to handle the HTTP header according to the type of authentication credentials.

  • DigestAuthenticationFilter can HttpSecurity# addFilter () to enable and configure related functions.

  • BearerTokenAuthenticationFilter: processing Token authentication.

  • BasicAuthenticationFilter: used to detect and deal with Http Baisc certification.

    • BasicAuthenticationFilter can HttpSecurity# httpBasic () and related methods to introduce its configuration object HttpBaiscConfigurer configuration.
  • RequestCacheAwareFilter: Used to resume requests that were interrupted due to login after the user has been successfully authenticated. When anonymously accessing a resource that requires authorization. Will jump to the authentication processing logic, at which point the request is cached. After the authentication logic is processed, the original resource request is retrieved from the cache and requested again.

    • The RequestCacheAwareFilter can be configured by using HttpSecurity#requestCache() and related methods to introduce its configuration object, RequestCacheConfigurer.
  • SecurityContextHolderAwareRequestFilter: to the packaging of the request object, added some security related methods.

    • SecurityContextHolderAwareRequestFilter can HttpSecurity# servletApi () and related methods to introduce its configuration object ServletApiConfigurer configuration.
  • JaasApiIntegrationFilter: Applies to JAAS (Java Authentication and Authorization Service). If the Authentication held in the SecurityContextHolder is a JaasAuthenticationToken, The JaasApiIntegrationFilter then continues to execute FilterChain using the Subject contained in the JaasAuthenticationToken.

  • RememberMeAuthenticationFilter: When the user accesses resources directly without login, the user’s information will be found out from the cookie. If Spring Security can recognize the Remember Me cookie provided by the user, the user will not need to fill in the user name and password, but directly log in to the system. It checks the SecurityContext for an Authentication object, and if it does, it skips to the next filter without doing anything. If not, check whether the request contains the cookie information of remember-me. If yes, the authentication information in the cookie is parsed to determine whether the permission is granted.

    • RememberMeAuthenticationFilter can HttpSecurity# rememberMe () and related methods to introduce its configuration object RememberMeConfigurer configuration.
  • AnonymousAuthenticationFilter: anonymous Authentication filter, whether exist in detecting SecurityContextHolder Authentication object, if not, will generate an anonymous Authentication object.

    • AnonymousAuthenticationFilter can HttpSecurity# anonymous () and related methods to introduce its configuration object AnonymousConfigurer configuration.
  • OAuth2AuthorizationCodeGrantFilter: the 2.0 authorization code model, used for processing the 2.0 authorization code response.

  • SessionManagementFilter: to test whether the user through the authentication, if certified, is through the SessionAuthenticationStrategy Session related management operations.

    • SessionManagementFilter can HttpSecurity# sessionManagement () and related methods to introduce its configuration object SessionManagementConfigurer configuration.
  • ExceptionTranslationFilter: can be used to capture all of the exception on the FilterChain, but only AccessDeniedException and AuthenticationException exception handling.

  • FilterSecurityInterceptor: web resources for security operations.

  • SwitchUserFilter: Mainly used for user switching.

Automatic configuration

According to the Spring Boot automatic configuration principle, It will automatically load the spring – the boot – autoconfigure. Jar/meta-inf/spring. Factories in key org. Springframework. Boot. Autoconfigure. EnableAutoConfig Configuration specifies an automatic configuration class. Looking at the file, you can see that there are several auto-configuration classes related to Spring Security:

org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,                                      \
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,                            \
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,                                \
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,                             \
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,                   \
org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration,                               \
org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration,                               \
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,                    \
org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,           \
org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,          \
org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration, \
Copy the code

Each configuration class injects a different Bean into the Spring container for Spring Security. Here we introduce emphatically SecurityFilterAutoConfiguration and SecurityAutoConfiguration configuration class, Because these two configuration classes automatically assemble The DelegatingFilterProxy and FilterChain into the Spring container.

Automatic assembly of FilterChainProxy

Below the configuration class SecurityAutoConfiguration, specific as follows:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
@EnableConfigurationProperties(SecurityProperties.class)
@Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class, SecurityDataConfiguration.class })
public class SecurityAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean(AuthenticationEventPublisher.class)
    public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
        return newDefaultAuthenticationEventPublisher(publisher); }}Copy the code

SecurityAutoConfiguration import 3 configuration class, pay attention to see WebSecurityEnablerConfiguration. Check the WebSecurityEnablerConfiguration configuration class, its source code is as follows:

@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(name = "springSecurityFilterChain")
@ConditionalOnClass(EnableWebSecurity.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@EnableWebSecurity
class WebSecurityEnablerConfiguration {}Copy the code

When no name in the Spring container for springSecurityFilterChain the conditions such as beans, will load the configuration class, at this point the @ EnableWebSecurity annotations into effect:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class, HttpSecurityConfiguration.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
    boolean debug(a) default false;
}
Copy the code

The @enableWebSecurity annotation imports four more configuration classes. Here’s a look at WebSecurityConfiguration:

@Configuration(proxyBeanMethods = false)
public class WebSecurityConfiguration implements ImportAware.BeanClassLoaderAware {.../**
     * Creates the Spring Security Filter Chain
     * @return the {@link Filter} that represents the security filter chain
     * @throws Exception
     */
    @Bean(name = "springSecurityFilterChain")
    public Filter springSecurityFilterChain(a) throws Exception {...return this.webSecurity.build(); }... }Copy the code

As you can see, WebSecurityConfiguration# springSecurityFilterChain () eventually created a name for springSecurityFilterChain Bean entity, The actual type of the Bean is actually FilterChainProxy, created by the WebSecurity#build() method.

In conclusion, SecurityAutoConfiguration configuration class generated a lot of beans entity, one of the most important one is the name for springSecurityFilterChain named FilterChainProxy object.

Auto assemble DelegatingFilterProxy

Here the configuration class SecurityFilterAutoConfiguration, its source code is as follows:

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(SecurityProperties.class)
@ConditionalOnClass({ AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class })
@AutoConfigureAfter(SecurityAutoConfiguration.class) Must be loaded SecurityAutoConfiguration / /
public class SecurityFilterAutoConfiguration {...@Bean
    @ ConditionalOnBean (name = "springSecurityFilterChain")
    public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration( SecurityProperties securityProperties) {... }... }Copy the code

As you can see, to load SecurityFilterAutoConfiguration before, must first be loaded configuration class SecurityAutoConfiguration, this configuration in front of the class have been introduced in detail, Main function is injected with a name for springSecurityFilterChain Bean, as a result, the SecurityFilterAutoConfiguration# securityFilterChainRegistration will take effect, The resulting a DelegatingFilterProxyRegistrationBean entity. DelegatingFilterProxyRegistrationBean ServletContextInitializer interface is achieved, When the system execution ServletWebServerApplicationContext. SelfInitialize initialized (), will, in turn, calls to: RegistrationBean.onStartup() –> DynamicRegistrationBean.register() –> AbstractFilterRegistrationBean. AddRegistration (), among them, AbstractFilterRegistrationBean# addRegistration () the source code is as follows:

protected Dynamic addRegistration(String description, ServletContext servletContext) {
    Filter filter = this.getFilter();
    return servletContext.addFilter(this.getOrDeduceName(filter), filter);
}
Copy the code

Enclosing getFilter () is the actual call DelegatingFilterProxyRegistrationBean# getFilter () method, its internal will create a DelegatingFilterProxy instance and returns, the source code is as follows:

public DelegatingFilterProxy getFilter(a) {
    return new DelegatingFilterProxy(this.targetBeanName, this.getWebApplicationContext()) {
        protected void initFilterBean(a) throws ServletException {}}; }Copy the code

As a result, AbstractFilterRegistrationBean# addRegistration () would eventually through ServletContext# addFilter will be injected into a DelegatingFilterProxy instance Servlet The FilterChain.

Above, is Spring Boot automatic assembly Spring Security related configuration source code analysis, for more details, please refer to:

conclusion

The mechanism of Spring Security is as follows:

  1. Register Filter: First, Spring automatically injects a DelegatingFilterProxy into the Servlet’s FilterChain.
  2. Request forward to Spring Security: When the request arrives, the DelegatingFilterProxy will automatically search the name in the Spring container for springSecurityFilterChain Filter entity, its actual type is named FilterChainProxy. DelegatingFilterProxy ultimately forwards the request to the FilterChainProxy.
  3. Find the SecurityFilterChain that matches the request processing: FilterChainProxy maintains a series of SecurityFilterChains. It will find the SecurityFilterChain that matches the request processing based on the request content.
  4. Request processing: After finding the first SecurityFilterChain that can handle the request, the system iterates through a series of Filters maintained by the SecurityFilterChain, allowing the Security Filters to process the request, complete authentication, authorization, and other functions.

A simple diagram of the Spring Security architecture