SpringSecurity实现原理及多过滤器链匹配规则详解

 更新时间:2025年10月21日 14:19:06   作者:henry_yang2018  
SpringSecurity通过FilterChainProxy管理多个过滤器链,根据请求的URI和RequestMatcher匹配,选择合适的过滤器链进行认证和授权处理,配置多个过滤器链需用@Order区分优先级,处理流程清晰高效

SpringSecurity的实现原理

Spring Security能完成各种认证和授权功能其实是依赖其底层的多个过滤器进行实现的,

借用官方的一张原理图如下:

但是实际上这些Filter并不是直接配置在tomcat中,其实他们都由一个叫做FilterChainProxy的类进行管理

这些Filter包括Spring Security默认生成的以及我们自己自定义的,通通都被封装成成一个个FilterChain并且保存在FilterChainProxy的一个成员变量中,一个FilterChain包含多个Filter,

FilterChainProxy包含多个FilterChain

原理图和代码如下:

public class FilterChainProxy extends GenericFilterBean {
    private static final Log logger = LogFactory.getLog(FilterChainProxy.class);
    private static final String FILTER_APPLIED =                FilterChainProxy.class.getName().concat(".APPLIED");
    private List<SecurityFilterChain> filterChains; //多个过滤器链
    private FilterChainProxy.FilterChainValidator filterChainValidator;
    private HttpFirewall firewall;
}

当一个请求过来的时候,Spring Security会根据规则匹配到第一个过滤器链,并使用它过滤该请求

这里又引申出一些问题,既然存在多过滤链的情况,那么用户怎么配置多个过滤链,并且SpringSecurity是怎么根据请求选择相应的过滤器链

如何配置多个过滤器链

其实配置多个过滤器链并不难,项目中每存在一个继承了WebSecurityConfigurerAdapter类的配置类,Spring Securiy就会生成一个过滤器链;

继承ResourceServerConfigurerAdapter类的配置类也会对应一个过滤器链,但是与继承了WebSecurityConfigurerAdapter类的配置类不同,

多个继承ResourceServerConfigurerAdapter类的配置类都共存于一个过滤器链中,也就是无论配置多少个ResourceServerConfigurerAdapter也仅仅存在一个对应的过滤链。

同时需要注意的是,存在多个WebSecurityConfigurerAdapter配置类的时候,需要用@Order进行区分,代表了它们之间的优先级

配置代码如下

@Configuration
@Order(200)
public class DefaultWebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        System.out.println("DefaultWebSecurityConfig "+http);
    }
}


@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        System.out.println("WebSecurityConfig "+http);
    }
}


@Configuration
public class ResourceSecurityConfig {

    public static final String RESOURCE_ID = "resource-server";

    @Resource
    private ResourceServerTokenServices resourceServerTokenServices;

    @Configuration
    @EnableResourceServer
    public class FirstResourceConfig extends ResourceServerConfigurerAdapter{
        //配置token服务以及资源ID
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            resources.resourceId(RESOURCE_ID)
                    .tokenServices(resourceServerTokenServices);
        }

        //配置访问控制
        @Override
        public void configure(HttpSecurity http) throws Exception {
            System.out.println("FirstResourceConfig "+http);
                    http
                    .authorizeRequests()
                    .antMatchers("/**").access("#oauth2.hasScope('server')")
            ;
        }

    }

    @Configuration
    @EnableResourceServer
    public class SecondResourceConfig extends ResourceServerConfigurerAdapter{
        //配置token服务以及资源ID
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            resources.resourceId(RESOURCE_ID)
                    .tokenServices(resourceServerTokenServices);
        }

        //配置访问控制
        @Override
        public void configure(HttpSecurity http) throws Exception {
            System.out.println("SecondResourceConfig "+http);
                    http
                    .authorizeRequests()
                    .antMatchers("/**").access("#oauth2.hasScope('server')")
            ;
        }

    }




}

上面的代码就是配置了三个过滤器链,最后一个虽然好像配置了两个过滤器链,但是前面已经说过了ResourceServerConfigurerAdapter的配置类其实都会被合并在同一个过滤器链

那我们怎么知道的的确确存在三个过滤器链呢,过滤器链都存在FilterChainProxy的成员变量中,我们只要debug一下看看它的成员变量就知道了,如下图:

每一个FilterChain都包含一个RequestMatcher,如下图

实际上过滤器链的RequestMatcher是和我们的配置有关系,前面我们的配置都是使用了.authorizeRequests()方法,其实这个就是代表AnyRequestMatcher这个类

我们改变一下配置可以看看RequestMatcher会有什么变化

@Configuration
@Order(200)
public class DefaultWebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        System.out.println("DefaultWebSecurityConfig "+http);
        http.antMatcher("/user")
                .authorizeRequests().anyRequest().permitAll()
                .and()
                .formLogin();
    }
}

我们把第一个配置类的规则改变了,看看debug会发生什么变化

它变成了AntPathRequestMatcher,这里可以看出我们的配置会直接影响到SpringSecurity为我们选择合适的过滤器链,如果改成上面的配置,其实就代表

DefaultWebSecurityConfig对应的过滤器链只匹配/user的请求,而其他两个过滤器链就是匹配/**的请求,虽然两个过滤器链都是匹配相同的地址,

但是他们的优先级不同,一般ResourceServerConfigurerAdapter类默认的优先级是@Order(3),而WebSecurityConfigurerAdapter类默认是@Order(100)

优先级的值越低就越先被匹配到,所以ResourceServerConfigurerAdapter会被先匹配到

如何匹配多个过滤器链

要知道SpringSecurity是怎样匹配一个合适的过滤链,我们先看下面的代码:

private List<Filter> getFilters(HttpServletRequest request) {
        Iterator var2 = this.filterChains.iterator();

        SecurityFilterChain chain;
        do {
            if (!var2.hasNext()) {
                return null;
            }

            chain = (SecurityFilterChain)var2.next();
        } while(!chain.matches(request)); //这里就是匹配过滤器链的方法

        return chain.getFilters();
    }

chain.matches()就是匹配一个过滤器链的方法,我们继续进入该方法:

public final class DefaultSecurityFilterChain implements SecurityFilterChain {
    private static final Log logger = LogFactory.getLog(DefaultSecurityFilterChain.class);
    private final RequestMatcher requestMatcher;
    private final List<Filter> filters;


    public boolean matches(HttpServletRequest request) {
        return this.requestMatcher.matches(request);//实际上是调用了requestMatcher的matches方法
    }
}

实际上是调用了RequestMatcher的matches方法,所以其实SpringSecurity在对一个请求选择一个过滤器链,就是通过调用每个过滤器链的RequestMatcher进行匹配

匹配到第一个成功的就是处理该请求的过滤器链

总结

所以我们可以得出SpringSecurity在处理一个请求的流程

1.获取该Request的URI地址

2.遍历所有FilterChain,并且用它的RequestMatcher匹配Request的URI地址

3.匹配到第一个合适的FilterChain,把该请求放到该过滤器链进行处理

4.过滤器链中的每一个过滤器获得请求,并对请求做相应的处理

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 关于Spring Actuator的简单测试

    关于Spring Actuator的简单测试

    这篇文章主要介绍了关于Spring Actuator的简单测试,Spring-Actuator 是spring下的程序监控系统,通过简单的配置就可以查看程序的相关信息,本文提供了相关配置,需要的朋友可以参考下
    2023-10-10
  • 详解Mybatis核心类SqlSessionFactory的构建

    详解Mybatis核心类SqlSessionFactory的构建

    这篇文章主要为大家详细介绍了Mybatis核心类SqlSessionFactory的构建过程,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2022-12-12
  • jvm虚拟机类加载机制详解

    jvm虚拟机类加载机制详解

    本文主要介绍了 jvm虚拟机类加载机制详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-04-04
  • Java强制保留两位小数的四种方法案例详解

    Java强制保留两位小数的四种方法案例详解

    这篇文章主要介绍了Java强制保留两位小数的四种方法案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-09-09
  • 基于JWT的spring boot权限验证技术实现教程

    基于JWT的spring boot权限验证技术实现教程

    这篇文章主要给大家介绍了关于基于JWT的spring boot权限验证技术实现的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • Spring与Dubbo搭建一个简单的分布式详情

    Spring与Dubbo搭建一个简单的分布式详情

    这篇文章主要介绍了Spring与Dubbo搭建一个简单的分布式详情,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-08-08
  • Java反射 PropertyDescriptor类案例详解

    Java反射 PropertyDescriptor类案例详解

    这篇文章主要介绍了Java反射 PropertyDescriptor类案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • Java实现人机猜拳游戏

    Java实现人机猜拳游戏

    这篇文章主要为大家详细介绍了Java实现人机猜拳游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-07-07
  • 详解@ConfigurationProperties如何装载到Spring容器中

    详解@ConfigurationProperties如何装载到Spring容器中

    这篇文章主要为大家详细介绍了@ConfigurationProperties该如何装载到Spring容器中,文中的示例代码讲解详细,需要的小伙伴可以参考一下
    2023-07-07
  • SpringBoot 添加JSP 支持并附带在IDEA下创建JSP文件【测试无误】

    SpringBoot 添加JSP 支持并附带在IDEA下创建JSP文件【测试无误】

    这篇文章主要介绍了SpringBoot 添加JSP 支持并附带在IDEA下创建JSP文件的相关知识,感兴趣的朋友跟随脚本之家小编一起学习吧
    2018-05-05

最新评论