2.过滤器链加载原理

简介: 本文深入解析Spring Security底层过滤器机制,揭示DelegatingFilterProxy如何加载FilterChainProxy,并最终将十五个安全过滤器封装进SecurityFilterChain,帮助理解框架自动配置背后的原理,为自定义认证页面打下基础。(239字)

通过前面十五个过滤器功能的介绍,对于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的认证操作呢?要完成此功能,首先要有一套自己的页面!

目录
相关文章
|
8月前
|
存储 弹性计算 Linux
阿里云服务器从零到精通的购买指南,云服务器购买流程及注意事项参考
对于许多初次接触阿里云服务器的用户而言,如何选择云服务器配置以及在选购过程中有哪些注意事项,是新手用户比较关心的问题。本文为大家展示阿里云服务器选购的完整指南,涵盖了通过云服务器ECS产品页下单的详细步骤,以及通过阿里云的活动选购价格比较实惠的云服务器。重点是介绍每一步的注意事项,以供初次选购阿里云服务器的个人开发者和企业用户参考,尽量一次选购好,避免出现买错从新买的情况出现。
|
4月前
|
负载均衡 应用服务中间件 Nacos
Nacos配置中心
本文详细介绍Nacos作为配置中心的实现原理与实战步骤,涵盖配置管理、热更新、共享配置优先级及集群搭建,帮助微服务应用实现配置动态化、高可用部署。
211 4
|
4月前
|
存储 NoSQL 关系型数据库
MongoDB相关概念
MongoDB是一款高性能、无模式的文档型NoSQL数据库,适用于高并发、海量数据、高可用场景。它以BSON格式存储数据,支持嵌入式文档、丰富查询、索引优化及水平扩展,广泛应用于社交、游戏、物联网等领域,具备高扩展性、高可用性与灵活的数据模型。
88 8
MongoDB相关概念
|
4月前
|
Java Spring
什么是WebFlux
Spring WebFlux 是 Spring Framework 5 引入的响应式Web框架,支持非阻塞、事件驱动的编程模型,适用于高并发场景,可运行于 Netty、Undertow 等服务器,提供注解式和函数式编程接口。
87 2
|
4月前
|
JSON Dubbo Java
Feign远程调用
本章介绍如何使用Feign替代RestTemplate实现更优雅的HTTP跨服务调用。通过引入Feign,结合注册中心与注解声明,解决硬编码、可读性差等问题,并支持日志、连接池等自定义配置。同时提出继承与抽取两种最佳实践,推荐将Feign客户端抽离为独立模块,提升代码复用性与维护性,助力微服务架构优化。
149 0
Feign远程调用
|
4月前
|
负载均衡 Java 数据安全/隐私保护
Gateway服务网关
网关是微服务架构的统一入口,核心功能包括请求路由、权限控制和限流。通过Spring Cloud Gateway可实现高效路由转发与过滤器处理,支持全局过滤与跨域解决方案,提升系统安全性和稳定性。(239字)
182 0
|
4月前
|
存储 Java 关系型数据库
微服务概述
本文对比单体应用与微服务架构,解析微服务的定义、核心特征及优缺点,介绍其技术选型与实现路径,帮助理解从单体到分布式架构的演进逻辑。
135 0
|
4月前
|
缓存 Java Nacos
@RefreshScope热更新原理
本文深入解析Spring Cloud中@RefreshScope注解实现配置热更新的原理。通过分析其组合注解特性,重点探讨@Scope(&quot;refresh&quot;)如何借助代理模式与缓存机制,在配置变更时触发Bean重建,结合Nacos动态刷新Environment,实现配置实时生效。
94 0
@RefreshScope热更新原理
|
4月前
|
前端开发 程序员 开发者
常见注解及使用说明
本文介绍了SpringMVC中@RequestMapping注解的作用与原理,它用于映射HTTP请求到控制器方法,实现前后端接口路径对应。并通过@GetMapping等派生注解进行简化,帮助开发者高效定义RESTful接口。
58 0
|
4月前
|
安全 Java 测试技术
2.OAuth2.0实战案例
本文介绍基于Spring Boot与Spring Cloud OAuth2搭建授权认证系统的过程,涵盖父工程创建、资源服务与授权服务配置,并实现授权码、简化、密码及客户端四种模式的测试流程,完成安全访问控制。
91 0
 2.OAuth2.0实战案例

热门文章

最新文章