过滤器链加载原理

简介: 本文深入解析Spring Security过滤器加载机制,通过源码分析DelegatingFilterProxy、FilterChainProxy与SecurityFilterChain的协作流程,揭示十五个安全过滤器如何自动装配并生效,帮助理解框架底层原理,为自定义认证页面奠定基础。

通过前面十五个过滤器功能的介绍,对于SpringSecurity简单入门中的疑惑是不是在心中已经有了答案了呀? 但新的问题来了!我们并没有配置这些过滤器啊?它们都是怎么被加载出来的?

1-DelegatingFilterProxy

我们在web.xml中配置了一个名称为springSecurityFilterChain的过滤器DelegatingFilterProxy,接下我直接对 DelegatingFilterProxy源码里重要代码进行说明,其中删减掉了一些不重要的代码,大家注意我写的注释就行了!

public class DelegatingFilterProxy extends GenericFilterBean {
    @Nullable 
    private String contextAttribute; 
    @Nullable 
    private WebApplicationContext webApplicationContext; 
    @Nullable 
    private String targetBeanName; 
    private boolean targetFilterLifecycle; 
    @Nullable 
    private volatile Filter delegate;//注:这个过滤器才是真正加载的过滤器 
    private final Object delegateMonitor; 
    //注:doFilter才是过滤器的入口,直接从这看! 
    public void doFilter(ServletRequest request, 
                         ServletResponse response, 
                         FilterChain 
                         filterChain) throws ServletException, IOException { 
        Filter delegateToUse = this.delegate; 
        if (delegateToUse == null) { 
            synchronized(this.delegateMonitor) { 
                delegateToUse = this.delegate; 
                if (delegateToUse == null) { 
                    WebApplicationContext wac = this.findWebApplicationContext(); 
                    if (wac == null) { 
                        throw new IllegalStateException("No WebApplicationContext found: no 
                        ContextLoaderListener or DispatcherServlet registered?"); 
                    } 
                    //第一步:doFilter中最重要的一步,初始化上面私有过滤器属性delegate 
                    delegateToUse = this.initDelegate(wac); 
                } 
                this.delegate = delegateToUse; 
            } 
        } 
        //第三步:执行FilterChainProxy过滤器 
        this.invokeDelegate(delegateToUse, request, response, filterChain); 
    } 
    //第二步:直接看最终加载的过滤器到底是谁 
    protected Filter initDelegate(WebApplicationContext wac) throws ServletException { 
        //debug得知targetBeanName为:springSecurityFilterChain 
        String targetBeanName = this.getTargetBeanName(); 
        Assert.state(targetBeanName != null, "No target bean name set"); 
        //debug得知delegate对象为:FilterChainProxy 
        Filter delegate = (Filter)wac.getBean(targetBeanName, Filter.class); 
        if (this.isTargetFilterLifecycle()) { 
            delegate.init(this.getFilterConfig()); 
      } 
    return delegate; 
    }
}

第二步debug结果如下:

由此可知,DelegatingFilterProxy通过springSecurityFilterChain这个名称,得到了一个FilterChainProxy过滤器, 最终在第三步执行了这个过滤器。

2-FilterChainProxy

注意代码注释!

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;
    //咿!?可以通过一个叫SecurityFilterChain的对象实例化出一个FilterChainProxy对象
    //这FilterChainProxy又是何方神圣?会不会是真正的过滤器链对象呢?先留着这个疑问!
    public FilterChainProxy(SecurityFilterChain chain) {
        this(Arrays.asList(chain));
    }
    //又是SecurityFilterChain这家伙!嫌疑更大了!
    public FilterChainProxy(List<SecurityFilterChain> filterChains) {
        this.filterChainValidator = new FilterChainProxy.NullFilterChainValidator();
        this.firewall = new StrictHttpFirewall();
        this.filterChains = filterChains;
    }
    //注:直接从doFilter看
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    throws IOException, ServletException {
        boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
        if (clearContext) {
            try {
                request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
                this.doFilterInternal(request, response, chain);
            } finally {
                SecurityContextHolder.clearContext();
                request.removeAttribute(FILTER_APPLIED);
            }
        } else {
            //第一步:具体操作调用下面的doFilterInternal方法了
            this.doFilterInternal(request, response, chain);
        }
    }
    private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain
                                  chain) throws IOException, ServletException {
        FirewalledRequest fwRequest =
        this.firewall.getFirewalledRequest((HttpServletRequest)request);
        HttpServletResponse fwResponse =
        this.firewall.getFirewalledResponse((HttpServletResponse)response);
        //第二步:封装要执行的过滤器链,那么多过滤器就在这里被封装进去了!
        List<Filter> filters = this.getFilters((HttpServletRequest)fwRequest);
        if (filters != null && filters.size() != 0) {
            FilterChainProxy.VirtualFilterChain vfc = new
            FilterChainProxy.VirtualFilterChain(fwRequest, chain, filters);
            //第四步:加载过滤器链
            vfc.doFilter(fwRequest, fwResponse);
        } else {
            if (logger.isDebugEnabled()) {
                logger.debug(UrlUtils.buildRequestUrl(fwRequest) 
                             + (filters == null ? " has no matching filters" : " has an empty filter list"));
            }
            fwRequest.reset();
            chain.doFilter(fwRequest, fwResponse);
        }
    }
    private List<Filter> getFilters(HttpServletRequest request) {
        Iterator var2 = this.filterChains.iterator();
        //第三步:封装过滤器链到SecurityFilterChain中!
        SecurityFilterChain chain;
        do {
            if (!var2.hasNext()) {
                return null;
            }
                chain = (SecurityFilterChain)var2.next();
    } while(!chain.matches(request));
    return chain.getFilters();
  }
}

第二步debug结果如下图所示,惊不惊喜?十五个过滤器都在这里了!

再看第三步,怀疑这么久!原来这些过滤器还真是都被封装进SecurityFilterChain中了。

3-SecurityFilterChain

最后看SecurityFilterChain,这是个接口,实现类也只有一个,这才是web.xml中配置的过滤器链对象!

//接口
public interface SecurityFilterChain {
    boolean matches(HttpServletRequest var1);
    List<Filter> getFilters();
}
//实现类
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 DefaultSecurityFilterChain(RequestMatcher requestMatcher,
                                      Filter... filters) {
        this(requestMatcher, Arrays.asList(filters));
    }
    
    public DefaultSecurityFilterChain(RequestMatcher requestMatcher, 
                                      List<Filter> filters) {
        logger.info("Creating filter chain: " + requestMatcher + ", " + filters);
        this.requestMatcher = requestMatcher;
        this.filters = new ArrayList(filters);
    }
    
    public RequestMatcher getRequestMatcher() {
        return this.requestMatcher;
    }
    
    public List<Filter> getFilters() {
        return this.filters;
    }
    
    public boolean matches(HttpServletRequest request) {
        return this.requestMatcher.matches(request);
    }
    
    public String toString() {
        return "[ " + this.requestMatcher + ", " + this.filters + "]";
    }
}

总结:通过此章节,我们对SpringSecurity工作原理有了一定的认识。但理论千万条,功能第一条,探寻底层,是为了更好的使用框架。 那么,言归正传!到底如何使用自己的页面来实现SpringSecurity的认证操作呢?要完成此功能,首先要有一套自己的页面!

相关文章
|
5月前
|
SQL Java 数据库连接
持久层框架MyBatisPlus
本课程系统讲解MyBatis-Plus(MP)的核心功能与实战应用,涵盖快速入门、条件构造器、Service接口、代码生成、分页插件等常用功能,结合Spring Boot实现CRUD操作与复杂查询,提升开发效率。
持久层框架MyBatisPlus
|
5月前
|
负载均衡 Java 数据安全/隐私保护
Gateway服务网关
网关是微服务架构的统一入口,核心功能包括请求路由、权限控制和限流。通过Spring Cloud Gateway可实现高效路由转发与过滤器处理,支持跨域配置,提升系统安全性和稳定性。
|
6月前
|
人工智能 自然语言处理 搜索推荐
数字人数字分身技术分析
数字人技术正打破虚实边界,融合AI、图形学与自然语言处理,打造可交互、可进化的“数字生命体”。从虚拟偶像到智能客服、智慧教育、医疗助手,其全链条技术突破推动人机共生新生态。
|
5月前
|
存储 JSON NoSQL
MongoDB常用命令
本文介绍了MongoDB常用操作命令,涵盖数据库与集合的创建、查看、删除,以及文档的增删改查、条件查询、投影、排序、分页和统计等功能,结合实例详细讲解了语法格式及使用注意事项,适用于初学者快速掌握MongoDB基本操作。
MongoDB常用命令
|
4月前
|
供应链 监控 安全
APT28全球钓鱼风暴突袭海事命脉:一封“船期变更”邮件,竟能瘫痪港口系统?
2025年底,APT28组织发动全球钓鱼攻击, targeting航运、港口与物流企业。通过伪造提单、海关通知等邮件,利用宏病毒与远程模板注入渗透内网,窃取敏感货运数据,甚至威胁关键基础设施安全。攻击横跨欧、美、亚,凸显海事网络安全短板。专家呼吁强化邮件防护、网络隔离与人员意识,构建纵深防御体系,警惕每一封“普通”邮件背后的国家级威胁。
319 4
|
8月前
|
JSON 监控 供应链
京东商品详情API参数构造指南:必填参数与自定义字段配置
京东商品详情API由京东开放平台提供,支持获取商品基础信息、价格库存、SKU规格等120+字段,适用于价格监控、库存管理等场景。接口采用HTTPS协议、JSON格式,数据延迟≤30秒,支持高并发。提供Python请求示例,便于快速接入。
|
4月前
|
API
快递查询-物流查询-快递物流查询API接口
本API提供快递物流轨迹实时查询服务,支持顺丰、中通、京东等1000+家快递公司,数据与官网同步。
463 0
|
4月前
|
人工智能 监控 Serverless
阿里云函数计算CPU/GPU实例浅休眠功能:2026年完整介绍及实测测评
在Serverless架构普及的2026年,阿里云函数计算(Function Compute,简称FC)推出的CPU/GPU实例浅休眠功能,成为解决“低延迟”与“低成本”矛盾的核心方案。该功能针对业务潮汐波动场景(如实时推理、音视频处理、定时任务等),让实例在无请求时自动进入休眠状态,仅支付极低保活费用,请求到来时毫秒级唤醒,兼顾服务质量与成本控制。本文结合2026年最新功能优化、实测数据及使用指南,为开发者和企业用户提供全面参考。
304 0
|
5月前
|
存储 弹性计算 固态存储
阿里云服务器租用价格:经济型、通用算力型、计算型等云服务器月付及年付优惠价格参考
阿里云服务器租用价格多少钱?云服务器可选择月租也可以选择按年租用,特定场景下还可以选择按量付费模式。在阿里云目前的云服务器活动中,轻量应用服务器2核2G200M带宽价格为38元1年。经济型e实例2核2G3M只要99元1年,通用算力型u1实例2核4G5M只要199元1年。通用算力型 u2a实例4核8G云服务器活动价格898.20元/1年起,4核16G配置1291.80元/1年起。本文为大家整理汇总了云服务器实例配置、带宽、云盘收费标准,经济型、通用算力型、计算型等实例规格云服务器月付及年付优惠价格,以供参考。
632 13