过滤器链加载原理

简介: 本文深入解析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的认证操作呢?要完成此功能,首先要有一套自己的页面!

相关文章
|
2月前
|
SQL Java 数据库连接
持久层框架MyBatisPlus
本课程系统讲解MyBatis-Plus(MP)的核心功能与实战应用,涵盖快速入门、条件构造器、Service接口、代码生成、分页插件等常用功能,结合Spring Boot实现CRUD操作与复杂查询,提升开发效率。
持久层框架MyBatisPlus
|
2月前
|
负载均衡 Java 数据安全/隐私保护
Gateway服务网关
网关是微服务架构的统一入口,核心功能包括请求路由、权限控制和限流。通过Spring Cloud Gateway可实现高效路由转发与过滤器处理,支持跨域配置,提升系统安全性和稳定性。
|
2月前
|
存储 JSON NoSQL
MongoDB常用命令
本文介绍了MongoDB常用操作命令,涵盖数据库与集合的创建、查看、删除,以及文档的增删改查、条件查询、投影、排序、分页和统计等功能,结合实例详细讲解了语法格式及使用注意事项,适用于初学者快速掌握MongoDB基本操作。
MongoDB常用命令
|
2月前
|
canal 缓存 关系型数据库
微服务阶段原理篇
本文介绍了电商系统中ES索引与MySQL数据同步的解决方案,重点阐述了基于Canal和MQ的异步同步机制。通过解析MySQL的binlog日志,Canal实现数据变更的实时捕获,并结合RabbitMQ保证消息顺序性,最终实现Elasticsearch索引的高效更新。该方案解耦了业务逻辑与索引维护,提升了系统性能与一致性。
 微服务阶段原理篇
|
2月前
|
负载均衡 算法 架构师
Ribbon负载均衡
负载均衡是高并发系统中的核心技术,通过水平扩展将流量分摊至多台服务器,提升系统性能与可用性。本文详解负载均衡概念、分类(硬件/软件)、常见算法及Ribbon的客户端实现原理,包括自定义策略与饥饿加载优化,助力深入理解微服务架构中的流量分发机制。
Ribbon负载均衡
|
2月前
|
消息中间件 Java Nacos
SpringCloud概述
Spring Cloud是微服务的统一解决方案,具备注解化、组件丰富、开箱即用等特点。其版本以地铁站命名,避免与子项目冲突。Spring Cloud Alibaba整合Nacos、Sentinel、Seata等阿里开源组件,提供更完整、稳定的微服务生态,成为当前主流技术选型。
|
2月前
|
Java 测试技术 Linux
生产环境发布管理
在一个大型团队中,生产发布是一件复杂的事情,从dev(前后端联调)-->test(测试集成&压力测试)-->pre(灰度测试)-->prod(生产环境)的多环境推进,以及生产环境的热更新、回滚等问题一直在困扰着各个公司,今天我将基于公司的自动化部署平台为大家讲解下我们是如何做到多环境部署。
生产环境发布管理
|
2月前
|
存储 负载均衡 算法
负载均衡算法
本文介绍多种负载均衡算法:随机、轮询、最小活跃数、源地址哈希及一致性哈希。涵盖适用场景、实现原理与代码示例,助你理解如何合理分配请求,提升系统稳定性与性能。
负载均衡算法
|
2月前
|
Java Maven Nacos
Eureka服务注册与发现
本文介绍了Spring Cloud中Eureka注册中心的搭建与使用,涵盖服务注册、多实例部署及常见问题解决,实现微服务间的动态发现与调用。
Eureka服务注册与发现
|
2月前
|
存储 Java 关系型数据库
微服务概述
本文对比单体应用与微服务架构,阐述微服务通过服务拆分、独立部署、技术自治等特性提升系统可维护性与扩展性,同时分析其优缺点及基于SpringCloud的实现方案,为架构转型提供理论指导。