SpringSecurity初始化过程

SpringSecurity初始化过程

SpringSecurity一定是被Spring加载的:

web.xml中通过ContextLoaderListener监听器实现初始化

xml 复制代码
<!--  初始化web容器-->
  <!--设置配置文件的路径-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  <!--配置Spring的监听器,默认只加载WEB-INF目录下的applicationContext.xml配置文件-->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

而spring配置文件中又引入了security的配置文件

xml 复制代码
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <context:component-scan base-package="com.shaoby.service"></context:component-scan>
    <bean name="bCryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
    //引入security配置文件
    <import resource="spring-security.xml"/>
</beans>

所以看ContextLoaderListener是如何加载的

Spring容器初始化

ContextLoaderListener中的configureAndRefreshWebApplicationContext方法

java 复制代码
   protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
        String configLocationParam;
        if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
            configLocationParam = sc.getInitParameter("contextId");
            if (configLocationParam != null) {
                wac.setId(configLocationParam);
            } else {
                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath()));
            }
        }

        wac.setServletContext(sc);
        //读取配置文件
        configLocationParam = sc.getInitParameter("contextConfigLocation");
        if (configLocationParam != null) {
            wac.setConfigLocation(configLocationParam);
        }

        ConfigurableEnvironment env = wac.getEnvironment();
        if (env instanceof ConfigurableWebEnvironment) {
            ((ConfigurableWebEnvironment)env).initPropertySources(sc, (ServletConfig)null);
        }

        this.customizeContext(sc, wac);
        //刷新容器,spring初始化的核心方法
        wac.refresh();
    }

refresh方法

java 复制代码
    public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();
            //创建bean工厂
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }

如何创建的bean工厂

spring通过loadBeanDefinitions方法完成初始化操作,它的实现方法可以通过xml配置文件或注解完成初始化

xml配置文件方法加载 XmlBeanDefinitionReader的loadBeanDefinitions方法中调用了doLoadBeanDefinitions方法
java 复制代码
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
        try {
            //通过流读取xml文件
            Document doc = this.doLoadDocument(inputSource, resource);
            //注册bean
            int count = this.registerBeanDefinitions(doc, resource);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Loaded " + count + " bean definitions from " + resource);
            }

            return count;
        } catch (BeanDefinitionStoreException var5) {
            throw var5;
        } catch (SAXParseException var6) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var6.getLineNumber() + " in XML document from " + resource + " is invalid", var6);
        } catch (SAXException var7) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var7);
        } catch (ParserConfigurationException var8) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var8);
        } catch (IOException var9) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var9);
        } catch (Throwable var10) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var10);
        }
    }
解析xml文件
java 复制代码
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(root)) {
            NodeList nl = root.getChildNodes();

            for(int i = 0; i < nl.getLength(); ++i) {
                Node node = nl.item(i);
                if (node instanceof Element) {
                    Element ele = (Element)node;
                    if (delegate.isDefaultNamespace(ele)) {
                        this.parseDefaultElement(ele, delegate);
                    } else {
                        //解析默认标签
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        } else {
            //解析自定义标签
            delegate.parseCustomElement(root);
        }

    }


private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        /如果是import标签,通过跟踪代码发现会去解析另一个配置文件,依次类推,直到没有import标签
        if (delegate.nodeNameEquals(ele, "import")) {
        this.importBeanDefinitionResource(ele);
        } else if (delegate.nodeNameEquals(ele, "alias")) {
        this.processAliasRegistration(ele);
        } else if (delegate.nodeNameEquals(ele, "bean")) {
        this.processBeanDefinition(ele, delegate);
        } else if (delegate.nodeNameEquals(ele, "beans")) {
        this.doRegisterBeanDefinitions(ele);
        }

        }

通过这段代码可以发现,spring会先解析默认标签,因此会先执行springsecurity的配置文件引入的操作

解析自定义标签
java 复制代码
    public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
        //获取标签
        String namespaceUri = this.getNamespaceURI(ele);
        if (namespaceUri == null) {
            return null;
        } else {
            //解析自定义标签,获得能解析该标签的HandlerResolver解析器
            NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
            if (handler == null) {
                this.error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
                return null;
            } else {
                return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
            }
        }
    }

springSecurity解析器SecurityNamespaceHandler

java 复制代码
/**不同的标签交给不同的解析器去解析*/
    private void loadParsers() {
        this.parsers.put("ldap-authentication-provider", new LdapProviderBeanDefinitionParser());
        this.parsers.put("ldap-server", new LdapServerBeanDefinitionParser());
        this.parsers.put("ldap-user-service", new LdapUserServiceBeanDefinitionParser());
        this.parsers.put("user-service", new UserServiceBeanDefinitionParser());
        this.parsers.put("jdbc-user-service", new JdbcUserServiceBeanDefinitionParser());
        this.parsers.put("authentication-provider", new AuthenticationProviderBeanDefinitionParser());
        this.parsers.put("global-method-security", new GlobalMethodSecurityBeanDefinitionParser());
        this.parsers.put("authentication-manager", new AuthenticationManagerBeanDefinitionParser());
        this.parsers.put("method-security-metadata-source", new MethodSecurityMetadataSourceBeanDefinitionParser());
        if (ClassUtils.isPresent("org.springframework.security.web.FilterChainProxy", this.getClass().getClassLoader())) {
            this.parsers.put("debug", new DebugBeanDefinitionParser());
            this.parsers.put("http", new HttpSecurityBeanDefinitionParser());
            this.parsers.put("http-firewall", new HttpFirewallBeanDefinitionParser());
            this.parsers.put("filter-security-metadata-source", new FilterInvocationSecurityMetadataSourceParser());
            this.parsers.put("filter-chain", new FilterChainBeanDefinitionParser());
            this.filterChainMapBDD = new FilterChainMapBeanDefinitionDecorator();
            this.parsers.put("client-registrations", new ClientRegistrationsBeanDefinitionParser());
        }

        if (ClassUtils.isPresent("org.springframework.messaging.Message", this.getClass().getClassLoader())) {
            this.parsers.put("websocket-message-broker", new WebSocketMessageBrokerSecurityBeanDefinitionParser());
        }

    }

http标签的解析,HttpSecurityBeanDefinitionParser

java 复制代码
    public BeanDefinition parse(Element element, ParserContext pc) {
        CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element));
        pc.pushContainingComponent(compositeDef);
        //注册FilterChainProxy
        registerFilterChainProxyIfNecessary(pc, pc.extractSource(element));
        //拿到FilterChainProxy
        BeanDefinition listFactoryBean = pc.getRegistry().getBeanDefinition("org.springframework.security.filterChains");
        List<BeanReference> filterChains = (List)listFactoryBean.getPropertyValues().getPropertyValue("sourceList").getValue();
        //将过滤器放到filterChains中
        filterChains.add(this.createFilterChain(element, pc));
        pc.popAndRegisterContainingComponent();
        return null;
        }

private BeanReference createFilterChain(Element element, ParserContext pc) {
        boolean secured = !"none".equals(element.getAttribute("security"));
        if (!secured) {
        if (!StringUtils.hasText(element.getAttribute("pattern")) && !StringUtils.hasText("request-matcher-ref")) {
        pc.getReaderContext().error("The 'security' attribute must be used in combination with the 'pattern' or 'request-matcher-ref' attributes.", pc.extractSource(element));
        }

        for(int n = 0; n < element.getChildNodes().getLength(); ++n) {
        if (element.getChildNodes().item(n) instanceof Element) {
        pc.getReaderContext().error("If you are using <http> to define an unsecured pattern, it cannot contain child elements.", pc.extractSource(element));
        }
        }

        return this.createSecurityFilterChainBean(element, pc, Collections.emptyList());
        } else {
        BeanReference portMapper = this.createPortMapper(element, pc);
        BeanReference portResolver = this.createPortResolver(portMapper, pc);
        ManagedList<BeanReference> authenticationProviders = new ManagedList();
        BeanReference authenticationManager = this.createAuthenticationManager(element, pc, authenticationProviders);
        boolean forceAutoConfig = isDefaultHttpConfig(element);
        HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, forceAutoConfig, pc, portMapper, portResolver, authenticationManager);
        AuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder(element, forceAutoConfig, pc, httpBldr.getSessionCreationPolicy(), httpBldr.getRequestCache(), authenticationManager, httpBldr.getSessionStrategy(), portMapper, portResolver, httpBldr.getCsrfLogoutHandler());
        httpBldr.setLogoutHandlers(authBldr.getLogoutHandlers());
        httpBldr.setEntryPoint(authBldr.getEntryPointBean());
        httpBldr.setAccessDeniedHandler(authBldr.getAccessDeniedHandlerBean());
        httpBldr.setCsrfIgnoreRequestMatchers(authBldr.getCsrfIgnoreRequestMatchers());
        authenticationProviders.addAll(authBldr.getProviders());
        List<OrderDecorator> unorderedFilterChain = new ArrayList();
        unorderedFilterChain.addAll(httpBldr.getFilters());
        unorderedFilterChain.addAll(authBldr.getFilters());
        unorderedFilterChain.addAll(this.buildCustomFilterList(element, pc));
        unorderedFilterChain.sort(new OrderComparator());
        this.checkFilterChainOrder(unorderedFilterChain, pc, pc.extractSource(element));
        List<BeanMetadataElement> filterChain = new ManagedList();
        Iterator var13 = unorderedFilterChain.iterator();

        while(var13.hasNext()) {
        OrderDecorator od = (OrderDecorator)var13.next();
        filterChain.add(od.bean);
        }

        return this.createSecurityFilterChainBean(element, pc, filterChain);
        }
        }
//注册FilterChainProxy
static void registerFilterChainProxyIfNecessary(ParserContext pc, Object source) {
        if (!pc.getRegistry().containsBeanDefinition("org.springframework.security.filterChainProxy")) {
        BeanDefinition listFactoryBean = new RootBeanDefinition(ListFactoryBean.class);
        listFactoryBean.getPropertyValues().add("sourceList", new ManagedList());
        pc.registerBeanComponent(new BeanComponentDefinition(listFactoryBean, "org.springframework.security.filterChains"));
        BeanDefinitionBuilder fcpBldr = BeanDefinitionBuilder.rootBeanDefinition(FilterChainProxy.class);
        fcpBldr.getRawBeanDefinition().setSource(source);
        fcpBldr.addConstructorArgReference("org.springframework.security.filterChains");
        fcpBldr.addPropertyValue("filterChainValidator", new RootBeanDefinition(DefaultFilterChainValidator.class));
        BeanDefinition fcpBean = fcpBldr.getBeanDefinition();
        pc.registerBeanComponent(new BeanComponentDefinition(fcpBean, "org.springframework.security.filterChainProxy"));
        //添加别名springSecurityFilterChain
        pc.getRegistry().registerAlias("org.springframework.security.filterChainProxy", "springSecurityFilterChain");
        }
        }

到这可以看到所有的过滤器被放到filterChain中

相关推荐
w_31234543 分钟前
自定义一个maven骨架 | 最佳实践
java·maven·intellij-idea
岁岁岁平安6 分钟前
spring学习(spring-DI(字符串或对象引用注入、集合注入)(XML配置))
java·学习·spring·依赖注入·集合注入·基本数据类型注入·引用数据类型注入
武昌库里写JAVA9 分钟前
Java成长之路(一)--SpringBoot基础学习--SpringBoot代码测试
java·开发语言·spring boot·学习·课程设计
Q_192849990616 分钟前
基于Spring Boot的九州美食城商户一体化系统
java·spring boot·后端
张国荣家的弟弟33 分钟前
【Yonghong 企业日常问题 06】上传的文件不在白名单,修改allow.jar.digest属性添加允许上传的文件SH256值?
java·jar·bi
ZSYP-S44 分钟前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
yuanbenshidiaos1 小时前
c++------------------函数
开发语言·c++
yuanbenshidiaos1 小时前
C++----------函数的调用机制
java·c++·算法
程序员_三木1 小时前
Three.js入门-Raycaster鼠标拾取详解与应用
开发语言·javascript·计算机外设·webgl·three.js
是小崔啊1 小时前
开源轮子 - EasyExcel01(核心api)
java·开发语言·开源·excel·阿里巴巴