Spring Security之springSecurityFilterChain注册到sevlet的过程(二)

1.基于上帝视角的分析

在上一篇中,分析了springSecurityFilterChain是在哪进行进行创建的,以及如何将这个bean注入到spring 容器中去。最后发现是通过spring-boot-autoconfigure.jar包中的org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration来完成的。


springSecurityFilterChain这个在哪加入到servlet容器中的,其实比较难找,我差不多通过一个小时的调试,最终才发现。


先公布成果:

向容器注入了一个DelegatingFilterProxyRegistrationBean,然后ServletWebServerApplicationContext将这个DelegatingFilterProxyRegistrationBean 解析出来,

加入到servletContext中,这样完成了springSecurityFilterChain注册成为servlet容器的过滤器的过程。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oYSbbikX-1625308330567)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f8c06ee6-b4d2-479c-8b84-1f1b3652380c/Untitled.png)]

下面我们将从tomcat到DelegatingFilterProxyRegistrationBean逐步解析。



2.spring 启动之tomcat服务器创建

  1. 在springboot启动时,会通过ServletWebServerApplicationContext创建一个web服务器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-06xPkvb0-1625308330571)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6ce996ee-5836-445b-9031-573c48589554/Untitled.png)]

  1. getSelfInitializer() 灵魂用法

这里通过函数式编程,将selfInitialize方法注入到ServletContextInitializer

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aMoI2XWV-1625308330573)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/bef120d4-b1b7-436e-aa5a-a93b44bdef34/Untitled.png)]

这里的this::selfInitialize 通过lambda创建一个匿名内部类,然后ServletContextInitializer的onStartUp方法在底层就自动改为调用selfInitialize方法了。
这个李代桃僵差点给我看懵了,需要学习的地方还有很多啊。

这个方法也很关键,后面真正进行filter注册,也是在这个方法中。



3.tomcat四大容器之StandardContext启动

StandardContext中进行TomcatStarter.onStartup()的调用。这个就意味着基于tomcat的servlet容器初始化开始了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9BuhpAWI-1625308330576)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4b47025f-8189-4bbb-a2cf-5e1441b9e459/Untitled.png)]



4. 内置Tomcat之TocatStarter执行ServletContainerInitializer启动

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wHnTC8My-1625308330579)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2233e742-f4b8-4cb3-b632-931485c95144/Untitled.png)]

从上图我们可以看到,在三个ServletContainerInitializer中,有一个ServletWebServerApplicationContext$lambda对象。

这个意味着,这是一个在ServletWebServerApplicationContext中创建的匿名lambda对象,而且这个对象是ServletContainerInitializer的子类。

那这里的initializer.onStartup(servletContext); 其实就是调用的ServletWebServerApplicationContextselfInitialize 方法。这个跟本篇的第二点不谋而合。

那最终,我们需要关注的就是selfInitialize 函数。



5. selfInitialize 做了些啥?

private void selfInitialize(ServletContext servletContext) throws ServletException {
		// 1.判断servletContext是否重复初始化
		// 2.servletContext 交给ServletWebServerApplicationContext管理的方法
		prepareWebApplicationContext(servletContext);
		// 1.设置sevletContext的作用域
		registerApplicationScope(servletContext);
		// 注册servletContext单例到spring容器中
		// 注册servletConfig单例到spring 容器中
		WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
		// 获取 ServletContextInitializer 子类,然后依次执行onStartup
		for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
			beans.onStartup(servletContext);
		}
	}

上文中主要可以分为两步:

1.servletContext的管理

2.ServletContextInitializer 继续初始化servletContext

这里我们要重点看看 getServletContextInitializerBeans() 有哪些ServletContextInitializer对象。



6.getServletContextInitializerBeans() 获取ServletContextInitializer 对象集合

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U99lHYOV-1625308330580)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/49613a33-3735-42a4-8097-b6f3a61536a2/Untitled.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rZnqL1yS-1625308330583)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/01aad71b-60a7-439f-be52-d9cb49b1b3fb/Untitled.png)]

那么首先看看addServletContextInitializerBeans 方法

private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
// 这个是针对多个初始化类型进行Bean获取操作		
for (Class<? extends ServletContextInitializer> initializerType : this.initializerTypes) {
		// 通过initializerType  获取spring容器中的ServletContextInitializer实例
		for (Entry<String, ? extends ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory,
				initializerType)) {
			// 对这里ServletContextInitializer的实例进行具体操作
			addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);
		}
	}
}

基于ServletContextInitializer类型去获取实例列表

private <T> List<Entry<String, T>> getOrderedBeansOfType(ListableBeanFactory beanFactory, Class<T> type,
			Set<?> excludes) {
		// 先获取实例别名
		String[] names = beanFactory.getBeanNamesForType(type, true, false);
		Map<String, T> map = new LinkedHashMap<>();
		for (String name : names) {
			// 1.是否在排除列表中 2.是否在作用域范围内
			if (!excludes.contains(name) && !ScopedProxyUtils.isScopedTarget(name)) {
				// 获取具体的bean
				T bean = beanFactory.getBean(name, type);
				if (!excludes.contains(bean)) {
					map.put(name, bean);
				}
			}
		}
		List<Entry<String, T>> beans = new ArrayList<>(map.entrySet());
		// 在对这些ServletContextInitializer根据@Order注解进行排序
		beans.sort((o1, o2) -> AnnotationAwareOrderComparator.INSTANCE.compare(o1.getValue(), o2.getValue()));
		return beans;
	}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-406ycNGM-1625308330584)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/154eeaa7-d5ba-4fdd-80d8-35d9f2f44a2b/Untitled.png)]

看到没,securityFilterChainRegistration -> {DelegatingFilterProxyRegistrationBean@7429} "springSecurityFilterChain urls=[/*] order=-100" 这个ServletContextInitializer 的实例,就是我们之前在SecurityFilterAutoConfiguration中发现的DelegatingFilterProxyRegistrationBean

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R7MkI6TN-1625308330586)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/37cf250e-35cf-45ff-92db-30c9e52c1288/Untitled.png)]

SecurityFilterAutoConfiguration 到 Servlet的容器的联动,基本都是通过ServletContextInitializer 来完成的。



体悟:

所以当我们想要开发一个基于spring mvc的过滤器插件时,就可以考虑使用继承AbstractFilterRegistrationBean来写自己的过滤器入口,然后注入spring容器即可~

现在还有最后一步,如何将获取的这些过滤器注册到servlet容器中去呢?




7. RegistrationBean#onStartup之ServletContextInitializer 注册到servletContext

在通过getOrderedBeansOfType 获取到ServletContextInitializer 实例列表后,我们需要回到ServletWebServerApplicationContext中,去执行每个ServletContextInitializer 实例的onStartup方法。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PeXRp06K-1625308330587)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/cceb8aaa-879c-4bd7-be43-4440616ed4fc/Untitled.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NG6wFKmc-1625308330588)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9762d67d-5aa8-446f-b719-5331ad6822fc/Untitled.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bXgpbn0C-1625308330588)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/23a1cb3a-953a-45cf-9e6f-fcdf2ecea10e/Untitled.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N14yrfRC-1625308330590)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/caf33b6c-a744-491e-b9f9-ad14bf256c13/Untitled.png)]

8.小结

在这个源码探索流程中,debug和大胆猜想很重要,当然经验也让我能更快的去探索到对应的内容。

本篇的分析得到的结论:

  1. springSecurityFilterChain 是在SecurityFilterAutoConfiguration 被注入到spring容器中
  2. 内置tomcat的TomcatStarter.onStartup方法实现了在tomcat启动中对ServletWebServerApplicationContext#selfInitialize方法的回调
  3. selfInitialize 完成了ServletContextInitializer服务实例的获取和过滤器注册到servlet容器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4laNNUjT-1625308330591)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ec5ce458-1e13-4361-bc51-46f3116474ff/oauth2_02springSecurityFilterChainservlet.png)]

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值