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中

相关推荐
IT技术分享社区10 分钟前
C#实战:使用腾讯云识别服务轻松提取火车票信息
开发语言·c#·云计算·腾讯云·共识算法
极客代码13 分钟前
【Python TensorFlow】入门到精通
开发语言·人工智能·python·深度学习·tensorflow
疯一样的码农20 分钟前
Python 正则表达式(RegEx)
开发语言·python·正则表达式
代码之光_198021 分钟前
保障性住房管理:SpringBoot技术优势分析
java·spring boot·后端
ajsbxi26 分钟前
苍穹外卖学习记录
java·笔记·后端·学习·nginx·spring·servlet
&岁月不待人&42 分钟前
Kotlin by lazy和lateinit的使用及区别
android·开发语言·kotlin
StayInLove1 小时前
G1垃圾回收器日志详解
java·开发语言
对许1 小时前
SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder“
java·log4j
无尽的大道1 小时前
Java字符串深度解析:String的实现、常量池与性能优化
java·开发语言·性能优化
爱吃生蚝的于勒1 小时前
深入学习指针(5)!!!!!!!!!!!!!!!
c语言·开发语言·数据结构·学习·计算机网络·算法