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.过滤器链中的每一个过滤器获得请求,并对请求做相应的处理

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

相关文章

  • SpringBoot防止大量请求攻击的实现

    SpringBoot防止大量请求攻击的实现

    在有些特定的时候需要加上IP访问时间限制,防止一个IP多次访问请求,本文主要介绍了SpringBoot防止大量请求攻击的实现,感兴趣的可以了解一下
    2021-11-11
  • 新手了解java基础知识(二)

    新手了解java基础知识(二)

    这篇文章主要介绍了Java基础知识,本文介绍了Java语言相关的基础知识、历史介绍、主要应用方向等内容,需要的朋友可以参考下,希望对你有所帮助
    2021-07-07
  • Java语言实现简单FTP软件 FTP远程文件管理模块实现(10)

    Java语言实现简单FTP软件 FTP远程文件管理模块实现(10)

    这篇文章主要为大家详细介绍了Java语言实现简单FTP软件,FTP远程文件管理模块的实现方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-04-04
  • 将JSON字符串数组转对象集合方法步骤

    将JSON字符串数组转对象集合方法步骤

    这篇文章主要给大家介绍了关于将JSON字符串数组转对象集合的方法步骤,文中通过代码示例介绍的非常详细,对大家的学习或者工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-08-08
  • Java URL自定义私有网络协议

    Java URL自定义私有网络协议

    URI与URL的区别 一.先来序言一段 二.协议的自定义的理解 三.自定义协议与URL的关系 四.URL自定义私有协议实战 五.后话,自定义mineType解析器
    2016-04-04
  • kafka内外网访问配置方式

    kafka内外网访问配置方式

    这篇文章主要介绍了kafka内外网访问配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • Java死锁代码实例及产生死锁必备的四个条件

    Java死锁代码实例及产生死锁必备的四个条件

    这篇文章主要介绍了Java死锁代码实例及产生死锁必备的四个条件,Java 发生死锁的根本原因是,在申请锁时发生了交叉闭环申请,synchronized在开发中最好不要嵌套使用,容易导致死锁,需要的朋友可以参考下
    2024-01-01
  • Java抽象类和抽象方法定义与用法实例详解

    Java抽象类和抽象方法定义与用法实例详解

    这篇文章主要介绍了Java抽象类和抽象方法定义与用法,结合实例形式详细分析了Java抽象类和抽象方法相关原理、定义、使用方法及操作注意事项,需要的朋友可以参考下
    2019-11-11
  • 快速上手Java单元测试框架JUnit5

    快速上手Java单元测试框架JUnit5

    今天给大家带来的是关于Java单元测试的相关知识,文章围绕着Java单元测试框架JUnit5展开,文中有非常详细的介绍及代码示例,需要的朋友可以参考下
    2021-06-06
  • 使用Mybatis对数据库进行单表操作的实现示例

    使用Mybatis对数据库进行单表操作的实现示例

    这篇文章主要介绍了使用Mybatis对数据库进行单表操作的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08

最新评论