Spring Security 6.0系列【3】源码篇之基于过滤器的基本原理

有道无术,术尚可求,有术无道,止于术。

本系列Spring Boot 版本 3.0.4

本系列Spring Security 版本 6.0.2

源码地址:https://gitee.com/pearl-organization/study-spring-security-demo

文章目录

    • 前言
    • 过滤器(Filter)
    • Security 过滤器(Security Filter)
    • 委派代理过滤器(DelegatingFilterProxy)
    • 过滤器链代理(FilterChainProxy)
    • Security 过滤器链(SecurityFilterChain)
    • 源码分析
      • 自动配置
      • DefaultSecurityFilterChain
      • springSecurityFilterChain

前言

Spring Security Servlet的支持是基于过滤器的,在请求到达Servlet之前,通过过滤器进行认证授权校验,用户合法且有权限,放行通过,反之会跳转到登录页或拒绝访问。所以本篇文档主要介绍Spring Security 中过滤器的相关知识。

类比JAVA Web中的过滤器Spring Security 中的过滤器进行了各种代理和增强,可以简单理解Security 中的过滤器结构如下所示:
Spring Security 6.0系列【3】源码篇之基于过滤器的基本原理_第1张图片
简要说明:

  1. 请求到DelegatingFilterProxy(代理过滤器)
  2. 调用FilterChainProxy(过滤器链代理)
  3. FilterChainProxy根据请求,调用匹配的SecurityFilterChainSecurity中的过滤器链)
  4. SecurityFilterChain中的多个有序的Security过滤器对请求进行处理,检验是否登录、是否授权… 并做出相应处理

过滤器(Filter)

想必大家对JAVA Web中的过滤器Filter)已经很熟悉了,作为三大组件之一,扮演者重要的角色。

一个简单的过滤器如下所示:

// 使用@ServletComponentScan添加在启动类上扫描该过滤器
@WebFilter(filterName = "myFilter", urlPatterns = {"/*"})
public class MyFilter implements Filter {

    // 过滤器对象进行初始化调用
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // ......
    }

    /**
     * 添加自定义过滤逻辑
     *
     * @param servletRequest  请求
     * @param servletResponse 响应
     * @param filterChain 过滤器链,由多个过滤器组成
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("对请求进行某些自定义操作");
        // 激活下一个过滤器的的doFilter 方法,最后一个激活Servlet 
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("对响应进行某些自定义操作");
    }

    // 过滤器销毁对象前被调用
    @Override
    public void destroy() {
        // ......
    }
}

Spring Security 6.0系列【3】源码篇之基于过滤器的基本原理_第2张图片

客户端向应用程序发送一个请求,运行容器创建一个 FilterChain(过滤器链),其中包含所有Filter实例和 Servlet。过滤器根据请求URI路径来处理请求响应

在一个Spring Boot Web应用程序中,一般只有一个Servlet实例,也就是DispatcherServlet ,但是一般都有多个过滤器,他们按照指定的顺序,共同协作。

Security 过滤器(Security Filter)

Spring Security 中的过滤器是通过SecurityFilterChain API 插入FilterChainProxy中的,Filter 实例的顺序非常重要。

Spring Security中所有的过滤器按照顺序如下所示(后续会详细介绍):

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

委派代理过滤器(DelegatingFilterProxy)

Spring Security 6.0系列【3】源码篇之基于过滤器的基本原理_第3张图片
Springspring-web模块中提供了对过滤器进行代理的类DelegatingFilterProxy,这样就可以很方便的使用Spring容器来管理过滤器。请求响应流程中,DelegatingFilterProxy 从容器中查找注册的过滤器 Bean 对象,然后调用 Bean 的过滤方法。

可以看到该类中包含了Spring容器对象和被代理的过滤器:
Spring Security 6.0系列【3】源码篇之基于过滤器的基本原理_第4张图片
在上一步骤中实现的过滤器,是使用Servlet容器自己的标准来注册,所以这时并不会被Spring容器管理,这时就可以使用DelegatingFilterProxy进行代理,实现代码如下:

@Component("myFilter")
public class MyFilter implements Filter {//...... }
@Configuration
public class MyConfig {

    @Bean
    public DelegatingFilterProxyRegistrationBean delegatingFilterProxyRegistrationBean(){
        DelegatingFilterProxyRegistrationBean filterProxy = new DelegatingFilterProxyRegistrationBean("myFilter");
        filterProxy.addUrlPatterns("/*");
        filterProxy.setOrder(-5);
        return filterProxy;
    }
}

过滤器链代理(FilterChainProxy)

Spring Security提供了FilterChainProxy 代理类, 是 Spring Security 使用的核心,用于代理Spring Security中所有的SecurityFilterChain ,在SecurityFilterChain 中又包含多个Spring Security声明的Filter
Spring Security 6.0系列【3】源码篇之基于过滤器的基本原理_第5张图片
FilterChainProxy 本质上是一个特殊的过滤器,通过DelegatingFilterProxy 进行代理,所以其也是一个Bean对象。在Security过滤器链中的过滤器,通常都是Bean对象,通过FilterChainProxy 进行注册,与直接向Servlet容器或 DelegatingFilterProxy 注册相比,FilterChainProxy 注册有很多优势:

  • 它为 Spring Security 的所有Servlet支持提供了一个起点,如果需要对 Spring SecurityServlet 支持进行故障诊断,可以在在FilterChainProxy中添加一个调试点。
  • 可以执行一些不被视为可有可无的任务。 例如,清除了 SecurityContext 以避免内存泄漏、应用Spring SecurityHttpFirewall来保护应用程序免受某些类型的攻击。
  • 在确定何时应该调用 SecurityFilterChain 方面提供了更大的灵活性。在Servlet容器中,Filter 实例仅基于URL被调用。FilterChainProxy 可以通过使用 RequestMatcher 接口,根据 HttpServletRequest 中的任何内容确定调用。

FilterChainProxy 在整个流程中的作用如下图:
Spring Security 6.0系列【3】源码篇之基于过滤器的基本原理_第6张图片

Security 过滤器链(SecurityFilterChain)

Servlet 中,一组过滤器组成FilterChain过滤器链,SecurityFilterChain就比较好理解了,是Spring Security提供的过滤器链,用于管理本身所有的过滤器,在上面的流程图中已有说明。SecurityFilterChain 可以被FilterChainProxy用来确定当前请求应该调用哪些 Spring Security Filter 实例。

在整个流程中, FilterChainProxy 决定应该使用哪个 SecurityFilterChain,只有第一个匹配的SecurityFilterChain被调用。

比如在下图中,如果请求的URL /api/messages/,那么会匹配到左边的SecurityFilterChain,如果都不匹配,则会调用支持/** SecurityFilterChain
Spring Security 6.0系列【3】源码篇之基于过滤器的基本原理_第7张图片

源码分析

自动配置

在入门篇中,我们只引入了一个spring-boot-starter-security依赖,就可以进行登录认证,得益于Spring Boot的自动配置。在spring-boot-autoconfigure模块中集成了对Spring Security的自动配置:
Spring Security 6.0系列【3】源码篇之基于过滤器的基本原理_第8张图片
默认的配置是由 SecurityAutoConfigurationUserDetailsServiceAutoConfiguration这两个自动配置类实现的。

SecurityAutoConfiguration 主要是导入 SpringBootWebSecurityConfiguration 配置:

@AutoConfiguration
@ConditionalOnClass({DefaultAuthenticationEventPublisher.class})
@EnableConfigurationProperties({SecurityProperties.class})
@Import({SpringBootWebSecurityConfiguration.class, SecurityDataConfiguration.class})
public class SecurityAutoConfiguration {
	// 认证事件发布者
    @Bean
    @ConditionalOnMissingBean({AuthenticationEventPublisher.class})
    public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
        return new DefaultAuthenticationEventPublisher(publisher);
    }
}

SpringBootWebSecurityConfiguration 配置类中,默认添加了 @EnableWebSecurity注解启用了Spring Security应用安全配置,并添加了一个SecurityFilterChain,添加了Http相关规则:

@Configuration( proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
class SpringBootWebSecurityConfiguration {

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnMissingBean(
        name = {"springSecurityFilterChain"}
    )
    @ConditionalOnClass({EnableWebSecurity.class})
    @EnableWebSecurity
    static class WebSecurityEnablerConfiguration {
    
    }

    @Configuration( proxyBeanMethods = false)
    @ConditionalOnDefaultWebSecurity
    static class SecurityFilterChainConfiguration {

        @Bean
        @Order(2147483642)
        SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        	// 配置所有的Http请求必须认证
            ((AuthorizeHttpRequestsConfigurer.AuthorizedUrl)http.authorizeHttpRequests().anyRequest()).authenticated();
            // 开启表单登录
            http.formLogin();
            // 开启Basic认证
            http.httpBasic();
            return (SecurityFilterChain)http.build();
        }
    }
}

UserDetailsServiceAutoConfiguration 则只是通过Yml配置配置文件生成了一个默认的用户,以便于开发测试:

@AutoConfiguration
@ConditionalOnClass({AuthenticationManager.class})
@ConditionalOnBean({ObjectPostProcessor.class})
@ConditionalOnMissingBean(
    value = {AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class, AuthenticationManagerResolver.class},
    type = {"org.springframework.security.oauth2.jwt.JwtDecoder", "org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector", "org.springframework.security.oauth2.client.registration.ClientRegistrationRepository", "org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository"}
)
public class UserDetailsServiceAutoConfiguration {

    private static final String NOOP_PASSWORD_PREFIX = "{noop}";
    private static final Pattern PASSWORD_ALGORITHM_PATTERN = Pattern.compile("^\\{.+}.*$");

    @Bean
    public InMemoryUserDetailsManager inMemoryUserDetailsManager(SecurityProperties properties, ObjectProvider<PasswordEncoder> passwordEncoder) {
        SecurityProperties.User user = properties.getUser();
        List<String> roles = user.getRoles();
        return new InMemoryUserDetailsManager(new UserDetails[]{User.withUsername(user.getName()).password(this.getOrDeducePassword(user, (PasswordEncoder)passwordEncoder.getIfAvailable())).roles(StringUtils.toStringArray(roles)).build()});
    }
    // 省略...........
}

SecurityFilterAutoConfiguration自动配置类中,则声明名称为springSecurityFilterChain的过滤器将会被代理:
Spring Security 6.0系列【3】源码篇之基于过滤器的基本原理_第9张图片

DefaultSecurityFilterChain

在上面说过SecurityFilterChain存放着所有的过滤器,Spring Security提供了默认实现类DefaultSecurityFilterChain,通过HttpSecurity.build方法构建,可以看到默认匹配所有请求,并默认存在15个过滤器:
Spring Security 6.0系列【3】源码篇之基于过滤器的基本原理_第10张图片

springSecurityFilterChain

SecurityFilterAutoConfiguration自动配置类中,声明名称为springSecurityFilterChain的过滤器将会被代理,那么这个过滤器是在哪里加载的呢?

首先在SpringBootWebSecurityConfiguration 配置类中,默认添加了 @EnableWebSecurity注解启用了Spring Security应用安全配置, @EnableWebSecurity会导入多个配置类:

@Import({WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class, HttpSecurityConfiguration.class})
@EnableGlobalAuthentication
public @interface EnableWebSecurity {
    boolean debug() default false;
}

WebSecurityConfiguration中,会构建这个过滤器:
Spring Security 6.0系列【3】源码篇之基于过滤器的基本原理_第11张图片
springSecurityFilterChain 会被被FilterChainProxy代理,注册为Bean,并存放了所有的SecurityFilterChain
Spring Security 6.0系列【3】源码篇之基于过滤器的基本原理_第12张图片
springSecurityFilterChain因为之前被声明过被DelegatingFilterProxy进行关联代理,最终经过层层代理,会生成完整的DelegatingFilterProxy类型过滤器,等待请求,并执行相关逻辑。

你可能感兴趣的:(Spring,Security,6.x,spring,servlet,spring,security)