美文网首页spring frameworkSpringboot
SpringSecurity的FilterChainProxy注

SpringSecurity的FilterChainProxy注

作者: 水煮鱼又失败了 | 来源:发表于2021-06-17 20:38 被阅读0次

    1 场景

    SpringSecurity起作用的原理依赖于FilterChainProxy类,其本身是个Servlet中的Filter,其中内置了拦截器链来对请求进行层层拦截,因此达到安全验证的目的。

    本文主要根据源码分析下,在SpringBoot的环境中springSecurity中的拦截器,如何达到安全验证的目的?

    2 相关版本

    此处的源码依赖SpringBoot的版本:2.3.3.RELEASE,相关依赖如下:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    

    3 基本原理

    3.1 组件之前的关系

    FilterChainProxy作为一个Filter,并没有直接配置在servlet中,而是借助了DelegatingFilterProxy对象进行了一层代理

    其中之间的关系如下所示(此图来自spring官网):

    1623849087684.png

    如上图所示,DelegatingFilterProxy也是Filter,其配置到Servlet的Filter中,对请求进行拦截。

    拦截到请求后,执行doFilter方法时,真正执行方法的,是被代理的对象FilterChainProxy(bean的name为springSecurityFilterChain)中的doFilter方法。如下图:

    1623930461679.png

    实现代码如下:

    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 = findWebApplicationContext();
                    if (wac == null) {
                        throw new IllegalStateException("No WebApplicationContext found: " +
                                                        "no ContextLoaderListener or DispatcherServlet registered?");
                    }
                    delegateToUse = initDelegate(wac);
                }
                this.delegate = delegateToUse;
            }
        }
    
        // 被代理对象(FilterChainProxy)执行真正的doFilter方法
        invokeDelegate(delegateToUse, request, response, filterChain);
    }
    
    // 初始化代理对象
    protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
        // 此处的targetName值为“springSecurityFilterChain”
        String targetBeanName = getTargetBeanName();
        Assert.state(targetBeanName != null, "No target bean name set");
        // 映射的被代理的bean类型为“FilterChainProxy”,name为“springSecurityFilterChain”
        Filter delegate = wac.getBean(targetBeanName, Filter.class);
        if (isTargetFilterLifecycle()) {
            delegate.init(getFilterConfig());
        }
        return delegate;
    }
    
    protected void invokeDelegate(
        Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {
        // 被代理对象(springSecurityFilterChain),执行真正的doFilter方法
        delegate.doFilter(request, response, filterChain);
    }
    

    3.2 为什么借助DelegatingFilterProxy进行代理

    使用DelegatingFilterProxy代理的Filter,可以受spring上下文环境管理,相对于传统的Filter,有如下优点:

    • 可以使用spring上下文环境中的bean
    • 可以很方便使用spring加载配置文件
    • 可以用spring管理Filter的生命周期(默认关闭,可设置targetFilterLifecycle为true开启)

    4 源码分析

    SpringBoot加载FilterChainProxy的代码调用关系如下图:

    SpringBoot中SpringGateway的过滤器链加载机制.png

    下文将对上图的代码结构图中标记位置①、②等进行详细记录

    • 代码①
    private void createWebServer() {
        WebServer webServer = this.webServer;
        ServletContext servletContext = getServletContext();
        if (webServer == null && servletContext == null) {
            ServletWebServerFactory factory = getWebServerFactory();
            // getSelfInitializer调用位置③代码
            this.webServer = factory.getWebServer(getSelfInitializer());
            getBeanFactory().registerSingleton("webServerGracefulShutdown",
                                               new WebServerGracefulShutdownLifecycle(this.webServer));
            getBeanFactory().registerSingleton("webServerStartStop",
                                               new WebServerStartStopLifecycle(this, this.webServer));
        }
        else if (servletContext != null) {
            try {
                // 调用位置②代码
                getSelfInitializer().onStartup(servletContext);
            }
            catch (ServletException ex) {
                throw new ApplicationContextException("Cannot initialize servlet context", ex);
            }
        }
        initPropertySources();
    }
    
    • 代码②
    // 代码①中的
    getSelfInitializer().onStartup(servletContext);
    
    • 代码③
    public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
                Class<? extends ServletContextInitializer>... initializerTypes) {
        this.initializers = new LinkedMultiValueMap<>();
        this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
            : Collections.singletonList(ServletContextInitializer.class);
        addServletContextInitializerBeans(beanFactory);
        addAdaptableBeans(beanFactory);
        List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
            .flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
            .collect(Collectors.toList());
        this.sortedList = Collections.unmodifiableList(sortedInitializers);
        logMappings(this.initializers);
    }
    
    • 代码④
    private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
            for (Class<? extends ServletContextInitializer> initializerType : this.initializerTypes) {
                for (Entry<String, ? extends ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory,
                        initializerType)) {
                    addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);
                }
            }
        }
    
    • 代码⑤
    private <T> List<Entry<String, T>> getOrderedBeansOfType(ListableBeanFactory beanFactory, Class<T> type,
                Set<?> excludes) {
        String[] names = beanFactory.getBeanNamesForType(type, true, false);
        Map<String, T> map = new LinkedHashMap<>();
        for (String name : names) {
            if (!excludes.contains(name) && !ScopedProxyUtils.isScopedTarget(name)) {
                T bean = beanFactory.getBean(name, type);
                if (!excludes.contains(bean)) {
                    map.put(name, bean);
                }
            }
        }
        List<Entry<String, T>> beans = new ArrayList<>(map.entrySet());
        beans.sort((o1, o2) -> AnnotationAwareOrderComparator.INSTANCE.compare(o1.getValue(), o2.getValue()));
        return beans;
    }
    
    • 代码⑥
    private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer,
                ListableBeanFactory beanFactory) {
        if (initializer instanceof ServletRegistrationBean) {
            Servlet source = ((ServletRegistrationBean<?>) initializer).getServlet();
            addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source);
        }
        else if (initializer instanceof FilterRegistrationBean) {
            Filter source = ((FilterRegistrationBean<?>) initializer).getFilter();
            addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
        }
        else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
            String source = ((DelegatingFilterProxyRegistrationBean) initializer).getTargetBeanName();
            addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
        }
        else if (initializer instanceof ServletListenerRegistrationBean) {
            EventListener source = ((ServletListenerRegistrationBean<?>) initializer).getListener();
            addServletContextInitializerBean(EventListener.class, beanName, initializer, beanFactory, source);
        }
        else {
            addServletContextInitializerBean(ServletContextInitializer.class, beanName, initializer, beanFactory,
                                             initializer);
        }
    }
    
    • 代码⑦
    private void addServletContextInitializerBean(Class<?> type, String beanName, ServletContextInitializer initializer,
                ListableBeanFactory beanFactory, Object source) {
        this.initializers.add(type, initializer);
        if (source != null) {
            // Mark the underlying source as seen in case it wraps an existing bean
            this.seen.add(source);
        }
        if (logger.isTraceEnabled()) {
            String resourceDescription = getResourceDescription(beanName, beanFactory);
            int order = getOrder(initializer);
            logger.trace("Added existing " + type.getSimpleName() + " initializer bean '" + beanName + "'; order="
                         + order + ", resource=" + resourceDescription);
        }
    }
    
    • 代码⑧
    public final void onStartup(ServletContext servletContext) throws ServletException {
        String description = getDescription();
        if (!isEnabled()) {
            logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
            return;
        }
        register(description, servletContext);
    }
    
    • 代码⑨
    // 类AbstractFilterRegistrationBean中的方法
    protected String getDescription() {
        Filter filter = getFilter();
        Assert.notNull(filter, "Filter must not be null");
        return "filter " + getOrDeduceName(filter);
    }
    
    • 代码⑩
    // 重要
    public DelegatingFilterProxy getFilter() {
        return new DelegatingFilterProxy(this.targetBeanName, getWebApplicationContext()) {
    
            @Override
            protected void initFilterBean() throws ServletException {
                // Don't initialize filter bean on init()
            }
    
        };
    }
    
    • 代码(11)
    protected final void register(String description, ServletContext servletContext) {
        D registration = addRegistration(description, servletContext);
        if (registration == null) {
            logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
            return;
        }
        configure(registration);
    }
    
    • 代码(12)
    // Filter被加入到servlet环境中
    protected Dynamic addRegistration(String description, ServletContext servletContext) {
        Filter filter = getFilter();
        return servletContext.addFilter(getOrDeduceName(filter), filter);
    }
    

    相关文章

      网友评论

        本文标题:SpringSecurity的FilterChainProxy注

        本文链接:https://www.haomeiwen.com/subject/rpfhyltx.html