首页 > 其他分享 >SpringBoot向web容器注入Servlet,Filter及SpringSecurity注册DelegatingFilterProxy

SpringBoot向web容器注入Servlet,Filter及SpringSecurity注册DelegatingFilterProxy

时间:2023-04-12 22:44:21浏览次数:47  
标签:web SpringBoot 容器 SpringSecurity Filter servletContext DelegatingFilterProxy Ser

SpringSecurity架构图可知SpringSecurity的过滤器与Web容器的过滤器是通过DelegatingFilterProxy接入的。由DelegatingFilterProxy代理了FilterChainProxy,FilterChainProxy包含了SpringSecurity的过滤器链。

 

那么DelegatingFilterProxy是怎么创建及如何加入到Web容器中?

 

看看SecurityFilterAutoConfiguration自动配置类:

@Bean
@ConditionalOnBean(
    name = {"springSecurityFilterChain"}
)
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(SecurityProperties securityProperties) {
    DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean("springSecurityFilterChain", new ServletRegistrationBean[0]);
    registration.setOrder(securityProperties.getFilter().getOrder());
    registration.setDispatcherTypes(this.getDispatcherTypes(securityProperties));
    return registration;
}

这个配置类创建了DelegatingFilterProxyRegistrationBean bean。

 

DelegatingFilterProxyRegistrationBean的类继承结构如下:

 

DelegatingFilterProxyRegistrationBean实现了ApplicationContextAware接口,可以注入ApplicationContext。还实现了ServletContextInitializer接口。ServletContextInitializer提供了配置Servlet 3.0+程序化接口。可以通过实现这个接口在SpringBoot启动时向web容器注入Servlet,Filter,Listener组件。

 

SpringBoot在启动时,会执行到AbstractApplicationContext#onRefresh()方法。ServletWebServerApplicationContext重写了onRefresh():

protected void onRefresh() {
	super.onRefresh();
	try {
		createWebServer();
	}
	catch (Throwable ex) {
		throw new ApplicationContextException("Unable to start web server", ex);
	}
}

调用createWebServer()创建web容器。createWebServer()会执行到this.webServer = factory.getWebServer(getSelfInitializer());此代码。最终调用selfInitialize方法:

private void selfInitialize(ServletContext servletContext) throws ServletException {
	prepareWebApplicationContext(servletContext);
	registerApplicationScope(servletContext);
	WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
	for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
		beans.onStartup(servletContext);
	}
}

调用getServletContextInitializerBeans()方法创建ServletContextInitializerBeans。在ServletContextInitializerBeans构造函数中会去Spring容器中获取ServletContextInitializer类型的所有Bean。再到selfInitialize调用ServletContextInitializer#onStartup方法。ServletContextInitializer#onStartup向web容器注入Servlet,Filter,Listener组件。

 

ServletContextInitializer的子类有ServletListenerRegistrationBean,DynamicRegistrationBean。ServletListenerRegistrationBean负责向web容器注入Listener组件。DynamicRegistrationBean负责向web容器注入Servlet,Filter组件。DynamicRegistrationBean的子类有ServletRegistrationBean,AbstractFilterRegistrationBean。
ServletRegistrationBean负责向web容器注入Servlet组件。AbstractFilterRegistrationBean负责向web容器注入Filter组件。如果想向web容器加入Servlet,可以继承ServletRegistrationBean并设置Servlet即可。

 

ServletContextInitializer的子类RegistrationBean重写了onStartup:

public final void onStartup(ServletContext servletContext) throws ServletException {
	String description = getDescription();
	if (!isEnabled()) {
		logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
		return;
	}
	register(description, servletContext);
}

register负责注入组件。

 

DynamicRegistrationBean#register(String description, ServletContext servletContext)

protected final void register(String description, ServletContext servletContext) {
	D registration = addRegistration(description, servletContext);
	if (registration == null) {
		logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
		return;
	}
	configure(registration);
}

configure配置异步web和初始化参数。addRegistration负责向web容器注入Servlet,Filter组件。

 

现在看看Filter的注入,即AbstractFilterRegistrationBean#addRegistration(String description, ServletContext servletContext):

protected Dynamic addRegistration(String description, ServletContext servletContext) {
	Filter filter = getFilter();
	return servletContext.addFilter(getOrDeduceName(filter), filter);
}

获取Filter然后加入ServletContext中。

 

AbstractFilterRegistrationBean子类又有FilterRegistrationBean,DelegatingFilterProxyRegistrationBean。FilterRegistrationBean用于SpringBoot向web容器注入Filter组件。如果我们想向web容器添加自定义filter,可以继承FilterRegistrationBean类,重写getFilter()方法即可。

 

DelegatingFilterProxyRegistrationBean#getFilter()

public DelegatingFilterProxy getFilter() {
	return new DelegatingFilterProxy(this.targetBeanName, getWebApplicationContext()) {

		@Override
		protected void initFilterBean() throws ServletException {
			// Don't initialize filter bean on init()
		}

	};
}

创建了DelegatingFilterProxy过滤器。注意DelegatingFilterProxy的构造函数的参数,targetBeanName是springSecurityFilterChain,还有WebApplicationContext。

 

DelegatingFilterProxy#doFilter

public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
		throws ServletException, IOException {

	// Lazily initialize the delegate if necessary.
	Filter delegateToUse = this.delegate;
	if (delegateToUse == null) {
		synchronized (this.delegateMonitor) {
			delegateToUse = this.delegate;
			if (delegateToUse == null) {
				WebApplicationContext wac = findWebApplicationContext();
				if (wac == null) {
					throw new IllegalStateException("No WebApplicationContext found: " +
							"no ContextLoaderListener or DispatcherServlet registered?");
				}
				delegateToUse = initDelegate(wac);
			}
			this.delegate = delegateToUse;
		}
	}

	// Let the delegate perform the actual doFilter operation.
	invokeDelegate(delegateToUse, request, response, filterChain);
}

initDelegate通过WebApplicationContext向Spring容器中获取名为targetBeanName的bean,即获取名为springSecurityFilterChain的Bean,也就是FilterChainProxy。invokeDelegate执行过滤器实际操作。

 

DelegatingFilterProxy#invokeDelegate

protected void invokeDelegate(
		Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
		throws ServletException, IOException {

	delegate.doFilter(request, response, filterChain);
}

执行delegate的doFilter方法也就是执行FilterChainProxy的doFilter方法。此时就将SpringSecurity Filter与web容器的Filter连接起来。

标签:web,SpringBoot,容器,SpringSecurity,Filter,servletContext,DelegatingFilterProxy,Ser
From: https://www.cnblogs.com/shigongp/p/17311457.html

相关文章

  • Springboot三种启动方式
    在https://start.spring.io/上创建一个springboot工程生成的代码中的启动方式咱们暂时定义为默认方式:/***@auther:lawt*@date:2018/12/117*@Description:默认启动方式*/@SpringBootApplicationpublicclassMicroServicesSpringBootApplication{publicstaticv......
  • springboot 中的 classpath 指的是什么路径?
    classpath其本质其实是指项目打包后的classes下的路径,一般用来指代“src/main/resources”下的资源路径。通常会在各种配置文件中使用【classpath】关键字,例如:yml配置文件:WebMvcConfigurer配置类:......
  • springboot整合阿里云OSS实现多线程下文件上传(aop限制文件大小和类型)
    内容涉及:springboot整合阿里云oss自定义注解及aop的使用:对上传文件格式(视频格式、图片格式)、不同类型文件进行大小限制(视频和图片各自自定义大小)线程池使用:阿里云OSS多线程上传文件阿里云OSS分片上传大文件 业务需求需求一:前端传递单个或多个小文件(这里......
  • 从0到1手把手教你ASP.NET Core Web API项目配置接口文档Swagger(二)
    传送门:从0到1手把手教你ASP.NETCoreWebAPI项目配置接口文档Swagger(一)一、设置Swagger页面为首页——开发环境我们虽然可以在输入/swagger后顺利的访问SwaggerUI页面,但是我们发现每次运行项目都会默认访问/weatherforecast这个接口,想要将启动页设为/swagger(或者其他......
  • Javaweb文件上传至服务器/从服务器下载
    Javaweb文件上传至服务器/从服务器下载思路图文件上传思路:也可以直接看代码判断是不是文件表单(判断form的enctype是不是="multipart/form-data"),因为只有文件表单才能上传文件创建DiskFileItemFactory对象,用于构建一个解析上传数据的工具对象创建一个解析上传......
  • JavaWeb技术栈图(web服务器+web容器是何物)
    JavaWeb技术栈图(web服务器+web容器是何物)两个重要概念web服务器+web容器什么是Web服务器?Tomcat服务器就是一个免费的开放源代码的Web应用服务器web服务实际上就是解析了客户端/浏览器发来的http请求,并将其做出一定的处理。比如说将请求头和请求体中的各个元素拆开打包成一......
  • JavaWeb之Servlet详解(以及浏览器调用 Servlet 流程分析图)
    Servlet1.什么是ServletServlet(java服务器小程序)他是由服务器端调用和执行的(一句话:是Tomcat解析和执行)他是用java语言编写的,本质就是Java类他是按照Servlet规范开发的(除了tomcat->Servletweblogic->Servlet)功能强大,可以完成几乎所有的网站功能2.开发......
  • JavaWeb中Servlet、web应用和web站点的路径细节("/"究竟代表着什么)
    JavaWeb中Servlet、web应用和web站点的路径细节("/"究竟代表着什么) 1开门见山新建一个tomcatweb项目,配置tomcat的虚拟目录,取默认值(/项目名_war_exploded)那么如果你的tomcat的默认站点(即http://localhost:8080)没有更改的话,这个项目的两个重要的根目录就出来了web站点根目......
  • javaweb-学习创建servlet
    Servlet创建、声明、映射,利⽤ServletContext统计⼀个⽹站的访问总量。1)、创建一个servelet选择要用到的方法2)、编辑serveletpackagecom.cont;importjava.io.IOException;importjava.io.PrintWriter;importjavax.servlet.ServletContext;importjavax.servlet.Ser......
  • web前端tips:使用 forEach 循环中的 return 语句会发生什么?
    近日,笔者在认真搬砖的过程中,突然遇到一个问题,请看大屏幕(代码):data(){return{statusList:[{code:"1",name:"已保存"},{code:"2",name:"已提交"}]......