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中