zoukankan      html  css  js  c++  java
  • Spring Security使用报错 No bean named 'springSecurityFilterChain' is defined

    转载:https://gist.github.com/linlihai/10219184#file-gistfile1-md

    近日来自己想基于maven搞一个有多子模块的archetype,就用比较流行的Spring mvc.archetype
    闲来无事加上Spring security可好?
    于是乎加上Spring security之后应用起不来了,日志如下:

    org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'springSecurityFilterChain' is defined
    	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:641)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1159)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:282)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
    	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:979)
    	at org.springframework.web.filter.DelegatingFilterProxy.initDelegate(DelegatingFilterProxy.java:324)
    	at org.springframework.web.filter.DelegatingFilterProxy.initFilterBean(DelegatingFilterProxy.java:235)
    	at org.springframework.web.filter.GenericFilterBean.init(GenericFilterBean.java:199)
    	at org.eclipse.jetty.servlet.FilterHolder.doStart(FilterHolder.java:99)
    

    网上google了一下,碰到这个问题的人很多,但是解决方案却没有一个说法,只能自己动手了.

    什么时候定义的'springSecurityFilterChain'

    日志报的是No bean named 'springSecurityFilterChain' is defined,那什么时候定义的springSecurityFilterChain?

    回答问题之前我们来回忆下Spriong IOC 怎么从xmlBeanFactory

    加载 Schema

    Srping 启动的时候 会去jar的META-INF下找Spring.schemas properties文件里面定义了 schema 到本地jar路径的一个映射关系

    解析Xml

    找到本地schema(dom树的描述结构), 去解析xml文件,校验配置是否合法,然后解析成一棵Dom树

    注册 Handler

    在jar的META-INF下找Spring.handlers properties文件里面定义了NamesapeceHandler, handler又注册了BeanDefinitionParser

    Dom ---> BeanDefinition

    每一个dom的节点都对应了一个BeanDefinitionParser,由他来解析成BeanDefinition

    对应到这个问题,找到org.springframework.security:spring-security-config下的Spring.handlers,发现NamesapeceHandler是SecurityNamespaceHandler.而SecurityNamespaceHandler注册一堆BeanDefinitionParser

    private void loadParsers() {
            // Parsers
            parsers.put(Elements.LDAP_PROVIDER, new LdapProviderBeanDefinitionParser());
            parsers.put(Elements.LDAP_SERVER, new LdapServerBeanDefinitionParser());
            parsers.put(Elements.LDAP_USER_SERVICE, new LdapUserServiceBeanDefinitionParser());
            parsers.put(Elements.USER_SERVICE, new UserServiceBeanDefinitionParser());
            parsers.put(Elements.JDBC_USER_SERVICE, new JdbcUserServiceBeanDefinitionParser());
            parsers.put(Elements.AUTHENTICATION_PROVIDER, new AuthenticationProviderBeanDefinitionParser());
            parsers.put(Elements.GLOBAL_METHOD_SECURITY, new GlobalMethodSecurityBeanDefinitionParser());
            parsers.put(Elements.AUTHENTICATION_MANAGER, new AuthenticationManagerBeanDefinitionParser());
            parsers.put(Elements.METHOD_SECURITY_METADATA_SOURCE, new MethodSecurityMetadataSourceBeanDefinitionParser());
    
            //当前的类加载器是否能够加载到 "org.springframework.security.web.FilterChainProxy"
            //来判断是否Web Application
            if(ClassUtils.isPresent(FILTER_CHAIN_PROXY_CLASSNAME, getClass().getClassLoader())) {
                parsers.put(Elements.DEBUG, new DebugBeanDefinitionParser());
                // 关键的http解析器
                parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser());
                parsers.put(Elements.HTTP_FIREWALL, new HttpFirewallBeanDefinitionParser());
                parsers.put(Elements.FILTER_INVOCATION_DEFINITION_SOURCE, new FilterInvocationSecurityMetadataSourceParser());
                parsers.put(Elements.FILTER_SECURITY_METADATA_SOURCE, new FilterInvocationSecurityMetadataSourceParser());
                parsers.put(Elements.FILTER_CHAIN, new FilterChainBeanDefinitionParser());
                filterChainMapBDD = new FilterChainMapBeanDefinitionDecorator();
            }
        }
    

    HttpSecurityBeanDefinitionParser相关代码

    static void registerFilterChainProxyIfNecessary(ParserContext pc, Object source) {
    	// 如果已经注册过了 就直接返回
            if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY)) {
                return;
            }
            BeanDefinition listFactoryBean = new RootBeanDefinition(ListFactoryBean.class);
            listFactoryBean.getPropertyValues().add("sourceList", new ManagedList());
            pc.registerBeanComponent(new BeanComponentDefinition(listFactoryBean, BeanIds.FILTER_CHAINS));
    
            BeanDefinitionBuilder fcpBldr = BeanDefinitionBuilder.rootBeanDefinition(FilterChainProxy.class);
            fcpBldr.getRawBeanDefinition().setSource(source);
            fcpBldr.addConstructorArgReference(BeanIds.FILTER_CHAINS);
            fcpBldr.addPropertyValue("filterChainValidator", new RootBeanDefinition(DefaultFilterChainValidator.class));
            BeanDefinition fcpBean = fcpBldr.getBeanDefinition();
            pc.registerBeanComponent(new BeanComponentDefinition(fcpBean, BeanIds.FILTER_CHAIN_PROXY));
            // BeanIds.FILTER_CHAIN_PROXY注册了一个 : org.springframework.security.filterChainProxy 
            // BeanIds.SPRING_SECURITY_FILTER_CHAIN :  springSecurityFilterChain
            // 为BeanIds.FILTER_CHAIN_PROXY注册了一个springSecurityFilterChain的别名
            pc.getRegistry().registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN);
        }
    

    看到了这里,如果说在加载Filter之前,Spring解析security.xml文件的话那么 Spring的beanDefinitionMap应该是能找的到这个BeanDefinition才对.
    为了证实这点,debug了下源码

    /**
     *  DefaultListableBeanFactory 
     */
    public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
    		BeanDefinition bd = this.beanDefinitionMap.get(beanName);
    		if (bd == null) {
    			if (this.logger.isTraceEnabled()) {
    				this.logger.trace("No bean named '" + beanName + "' found in " + this);
    			}
    			throw new NoSuchBeanDefinitionException(beanName);
    		}
    		return bd;
    	}
    

    源码中的 beanDefinitionMap.size()==0,结果已经很明朗了Filter的加载在Security.xml之前了.
    来看下web.xml的配置吧.

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns="http://java.sun.com/xml/ns/javaee"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
             version="3.0">
    
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:spring-*.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>*.htm</url-pattern>
        </servlet-mapping>
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
        <filter>
            <filter-name>springSecurityFilterChain</filter-name>
            <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>springSecurityFilterChain</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    </web-app>
    

    servlet 规范

    Servlet规范中 web.xml内加载的顺序是 context-param-->listener-->filter-->servlet.

    在web.xml的配置文件的加载依托给了DispatcherServlet,而servlet的加载顺序是在filter后面的故找不到springSecurityFilterChainBeanDefinition

    故将资源加载依托在context-param,问题就解决了

    具体操作:

    在spring-security.xml中注册配置类

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    
        <bean name="webSecurityConfig" class="cc.landfill.crowd.mvc.config.WebSecurityConfig"/>
    </beans>
    

    在web.xml中的context 中加载该xml文件,后加载的delegatingFilterProxy就能在容器中找到springSecurityFilterChain

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-persist-*.xml,classpath:spring-security.xml</param-value>
    </context-param>
    
  • 相关阅读:
    [kuangbin带你飞]专题十六 KMP & 扩展KMP & ManacherK
    [kuangbin带你飞]专题十六 KMP & 扩展KMP & Manacher J
    [kuangbin带你飞]专题十六 KMP & 扩展KMP & Manacher I
    pat 1065 A+B and C (64bit)(20 分)(大数, Java)
    pat 1069 The Black Hole of Numbers(20 分)
    pat 1077 Kuchiguse(20 分) (字典树)
    pat 1084 Broken Keyboard(20 分)
    pat 1092 To Buy or Not to Buy(20 分)
    pat 1046 Shortest Distance(20 分) (线段树)
    pat 1042 Shuffling Machine(20 分)
  • 原文地址:https://www.cnblogs.com/land-fill/p/13451309.html
Copyright © 2011-2022 走看看