Spring Security Principle (4) - Filter

Overview

Spring Security Principle (1) - Preliminary Exploration of Spring Security Principle (2) - Authentication Spring Security Principle (3) - Authorization Spring Security Principle (4) - Filter Spring Security Principle (5) - Extension and Configuration

In the previous article, we have basically understood how to use Spring Security's basic authentication (Authentication) and authorization (Authority)

However, we don't know exactly how Spring Security executes these processes.

In this article, we take SpringBoot as an example to sort out the process of Spring Security from starting to executing these processes.

Pre-knowledge

We know that Spring Security completes its core process through Filter. but:

  1. What Filters does Spring Security have?
  2. How are these Filters injected into the container?
  3. How do we customize our own Filter?

web.xml configuration

We have already introduced it before. At first, if we want to configure Filter, it is usually through web.xml:

<filter>  
   <filter-name>deleFilter</filter-name>  
   <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
   <init-param>  
      <param-name>targetBeanName</param-name>  
      <param-value>spring-bean-name</param-value>  
   </init-param>  
</filter>          
<filter-mapping>  
   <filter-name>deleFilter</filter-name>  
   <url-pattern>/*</url-pattern>  
</filter-mapping>  

Add Filter to SpringBoot

In SpringBoot, you can inject custom Filters through @WebFilter and @ServletComponentScan annotations.

@WebFilter(filterName = "myFilter",urlPatterns = "/*")
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
    }

    @Override
    public void destroy() {
    }
}

@SpringBootApplication
@ServletComponentScan(basePackages = "vip.mycollege.filter")
public class StartApplication {

    public static void main(String[] args) {
        SpringApplication.run(StartApplication.class, args);
    }
}

You can also inject a custom Filter by means of FilterRegistrationBean.

@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean filterRegistrationBean(){
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new MyFilter());
        bean.addUrlPatterns("/*");
        return bean;
    }
}

You can also use the DelegatingFilterProxyRegistrationBean method.

@Configuration
public class FilterConfig {
    @Bean("proxyFilter")
    public Filter filter (){
        return new Filter() {
            @Override
            public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
            }

            @Override
            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
            }

            @Override
            public void destroy() {
            }
        
    @Bean
    public DelegatingFilterProxyRegistrationBean delegatingFilterProxyRegistrationBean(){
        DelegatingFilterProxyRegistrationBean bean = new DelegatingFilterProxyRegistrationBean("proxyFilter");
        bean.addUrlPatterns("/*");
        return bean;
    }
}

Both DelegatingFilterProxyRegistrationBean and FilterRegistrationBean inherit AbstractFilterRegistrationBean, which is a RegistrationBean from the name, which means that it will be injected when the servlet container starts.

DelegatingFilterProxyRegistrationBean will register a DelegatingFilterProxy in the Servlet container to proxy a Filter bean with a specified name in the Spring IoC container.

FilterChainProxy

SpringBoot has an automatic configuration class of SecurityFilterAutoConfiguration, which will configure a DelegatingFilterProxyRegistrationBean whose name is springSecurityFilterChain. The url-pattern of this class defaults to /*, which means that all requests will be filtered.

name is springSecurityFilterChain what the hell is it?

The answer is: FilterChainProxy.

This class is registered in the registerFilterChainProxyIfNecessary method of HttpSecurityBeanDefinitionParser.

HttpSecurityBeanDefinitionParser is also a BeanDefinitionParser, so it builds the Filter class through the parse method.

The whole process is now clear:

  1. SpringBoot made a DelegatingFilterProxyRegistrationBean through the automatic configuration class
  2. DelegatingFilterProxyRegistrationBean will register a DelegatingFilterProxy when the servlet starts
  3. DelegatingFilterProxy will intercept all requests by default, and then hand over a FilterChainProxy aliased as springSecurityFilterChain
  4. FilterChainProxy is holding a list of SecurityFilterChain
  5. The SecurityFilterChain itself holds a list of Filters, which can be used to find out the Request matching the url and send it to filters for processing.

In addition to holding filters, FilterChainProxy has a built-in StrictHttpFirewall, an HTTP firewall, by default. It adopts strict mode. When encountering any suspicious request, it will reject the request by throwing an exception RequestRejectedException.

Now we know how Spring Security collects and utilizes Filters.

However, what Filters did Spring Security get behind our backs?

I just want to say a lot. It is very simple to know which ones are. Hit a breakpoint in FilterChainProxy, debug, and look at the list of filters in the filterChains variable to see which filters are there.

By default, filterChains has only one filter, which is DefaultSecurityFilterChain. You can see by the name that this is a SecurityFilterChain. It contains a list of Filters. The default is:

  1. WebAsyncManagerIntegrationFilter: Integrate with WebAsyncManager that handles asynchronous request mapping
  2. SecurityContextPersistenceFilter: Pre-request save and post-request clear security context in SecurityContextHolder
  3. HeaderWriterFilter: Add header information to the response
  4. CsrfFilter: Handling Cross-Site Request Forgery
  5. LogoutFilter: handles logout
  6. UsernamePasswordAuthenticationFilter: handles form-based logins
  7. DefaultLoginPageGeneratingFilter: Generates a default login page if no login page is configured
  8. DefaultLogoutPageGeneratingFilter: If there is no logout page, generate the default logout page
  9. BasicAuthenticationFilter: handles HTTP BASIC authentication
  10. RequestCacheAwareFilter: Cache that handles requests
  11. SecurityContextHolderAwareRequestFilter: Wraps the request object request
  12. AnonymousAuthenticationFilter: Detect whether SecurityContextHolder has Authentication, if not, provide an anonymous Authentication
  13. SessionManagementFilter: filter for managing sessions
  14. ExceptionTranslationFilter: handles AccessDeniedException and AuthenticationException exceptions
  15. FilterSecurityInterceptor: Permission verification related

Important Filter

UsernamePasswordAuthenticationFilter

UsernamePasswordAuthenticationFilter itself has nothing to say, it is a Filter, but because it is used a lot, let's talk about it.

Filter must first look at the doFilter method. The main authentication logic of UsernamePasswordAuthenticationFilter is in attemptAuthentication:

@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
        throws AuthenticationException {
    if (this.postOnly && !request.getMethod().equals("POST")) {
        throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
    }
    String username = obtainUsername(request);
    username = (username != null) ? username : "";
    username = username.trim();
    String password = obtainPassword(request);
    password = (password != null) ? password : "";
    UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
    setDetails(request, authRequest);
    return this.getAuthenticationManager().authenticate(authRequest);
}

It is very simple to get the fields of username and password from the request, encapsulate it as UsernamePasswordAuthenticationToken, and then throw it to the AuthenticationManager to perform authentication. Of course, the final authentication logic must be performed by an AuthenticationProvider like DaoAuthenticationProvider.

FilterSecurityInterceptor

FilterSecurityInterceptor is mainly used for permission verification, and the specific authentication logic is mainly in AbstractSecurityInterceptor.

FilterSecurityInterceptor is also a Filter, so let's look at the doFilter method first and call invoke:

public void invoke(FilterInvocation filterInvocation) throws IOException, ServletException {
        //一次请求中避免重复检查
		if (isApplied(filterInvocation) && this.observeOncePerRequest) {
			filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
			return;
		}
		// 第一次调用,先设置标记,避免重复调用
		if (filterInvocation.getRequest() != null && this.observeOncePerRequest) {
			filterInvocation.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
		}
        // 业务逻辑调用之前,执行检查鉴权操作主要就是在这里面完成
		InterceptorStatusToken token = super.beforeInvocation(filterInvocation);
		try {
            // 执行具体的业务逻辑
			filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
		}
		finally {
			super.finallyInvocation(token);
		}
        // 业务逻辑调用之后,主要是处理返回结果
		super.afterInvocation(token, null);
	}

FilterInvocation is a simple encapsulation of FilterInvocation, ServletResponse, and FilterChain.

We see that the logic of the whole invoke is very clear, much like the around structure of AOP.

ExceptionTranslationFilter

The logic of ExceptionTranslationFilter is a bit strange, it mainly deals with AccessDeniedException and AuthenticationException. But it is not to deal with the exception generated in front of it, but the exception generated by the Filter behind it, because if the Filter in front of it is abnormal, it cannot reach it at all.

Behind it, there is only FilterSecurityInterceptor by default, which mainly generates AccessDeniedException authorization exception. AuthenticationException is because there is a re-authentication process.

filter

  • WebAsyncManagerIntegrationFilter
  • SecurityContextPersistenceFilter
  • ChannelProcessingFilter
  • ConcurrentSessionFilter
  • HeaderWriterFilter
  • CorsFilter
  • CsrfFilter
  • LogoutFilter
  • OAuth2AuthorizationRequestRedirectFilter
  • Saml2WebSsoAuthenticationRequestFilter
  • X509AuthenticationFilter
  • AbstractPreAuthenticatedProcessingFilter
  • HomeAuthenticationFilter
  • OAuth2LoginAuthenticationFilter
  • Saml2WebSsoAuthenticationFilter
  • UsernamePasswordAuthenticationFilter
  • ConcurrentSessionFilter
  • OpenIDAuthenticationFilter
  • DefaultLoginPageGeneratingFilter
  • DefaultLogoutPageGeneratingFilter
  • DigestAuthenticationFilter
  • BearerTokenAuthenticationFilter
  • BasicAuthenticationFilter
  • RequestCacheAwareFilter
  • SecurityContextHolderAwareRequestFilter
  • JaasApiIntegrationFilter
  • RememberMeAuthenticationFilter
  • AnonymousAuthenticationFilter
  • OAuth2AuthorizationCodeGrantFilter
  • SessionManagementFilter
  • ExceptionTranslationFilter
  • SwitchUserFilter
  • FilterSecurityInterceptor

Documentation

Spring Security official documentation

{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324215311&siteId=291194637