在很多spring security框架学习资料里面,我们都会看到一句话那就是在web.xml中配置加载spring security框架的时候,所配置的Filter名字必须是springSecurityFilterChain,如果我们的名字没有使用固定的springSecurityFilterChain,比如名字改成springSecurityFilterChain1,如图:
这个时候加载过程中就会报错
从报错信息中我们可以得知,如果我们使用的名称是springSecurityFilterChain,那么我们就应该去加载一个id为springSecurityFilterChain的bean。但是,查看我们的spring-security.xml的配置文件,你根本找不到这个id为springSecurityFilterChain的bean,甚至连bean标签都找不到,如下便是spring-security.xml的内容
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:security="http://www.springframework.org/schema/security" 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 http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <security:http> <security:intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')" /> <!-- 开启表单登录: 会自动生成一个登录页面 --> <security:form-login /> </security:http> <security:authentication-manager> <security:authentication-provider> <security:user-service> <security:user name="admin" password="admin" authorities="ROLE_ADMIN"/> </security:user-service> </security:authentication-provider> </security:authentication-manager> </beans>
在查找了很多资料和分析了spring security框架源码之后,终于找到了这个隐藏着的id为springSecurityFilterChain的bean是如何被加载的
首先,我们看到spring-security.xml中有这样一个标签 <security:http>,然后我们从配置文件上可以看出,这里的security实际上是一个url的别名,这个url就是配置文件开头部分写着的xmlns:security="http://www.springframework.org/schema/security,也就是说<security:http>等价于<http://www.springframework.org/schema/security:http>
然后我们可以在spring security的org.springframework.security.config包下面找到一个名为spring.handlers的文件
点开这个文件,我们可以看到如下内容
由此可以得知,http://www.springframework.org/schema/security这个url实际上是指向了org.springframework.security.config包下的SecurityNameSpaceHandler类,接下来我们找到这个类,可以在这个类中找到如下这段代码
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(); } if (ClassUtils.isPresent("org.springframework.messaging.Message", this.getClass().getClassLoader())) { this.parsers.put("websocket-message-broker", new WebSocketMessageBrokerSecurityBeanDefinitionParser()); } }
其中this.parsers.put("http", new HttpSecurityBeanDefinitionParser());这句代码就是解决我们问题的关键,从这句代码可以得知,在是用属性为http的标签的时候,实际上是去创建了一个HttpSecurityBeanDefinitionParser类的对象,接下来我们找到这个类,可以在这个类中看到段静态代码,内容如下:
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"));
pc.getRegistry().registerAlias("org.springframework.security.filterChainProxy", "springSecurityFilterChain");
}
}
可见,在创建HttpSecurityBeanDefinitionParser类的对象的时候,就已经注册了一个名为springSecurityFilterChain的bean了