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

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连接起来。

posted @ 2023-04-12 22:39  shigp1  阅读(336)  评论(0)    收藏  举报