1. 程式人生 > 其它 >Spring Security 原始碼學習(二): Spring Security自動配置(初始化流程)

Spring Security 原始碼學習(二): Spring Security自動配置(初始化流程)

【深度好文】: 「和耳朵」SpringSecurity是如何代理過濾器鏈的?

1. 自動配置security的bean資訊

SpringBoot自動配置實現原理

下面是 Spring Boot autoconfigure 自動建立的配置類資訊

2. springSecurityFilterChain 初始化

1. springSecurityFilterChain 定義

WebSecurityConfiguration中定義了 springSecurityFilterChain 這個bean

@Configuration(proxyBeanMethods = false)
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {

	@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	public Filter springSecurityFilterChain() throws Exception {
		boolean hasConfigurers = webSecurityConfigurers != null
				&& !webSecurityConfigurers.isEmpty();
		if (!hasConfigurers) {
			WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
					.postProcess(new WebSecurityConfigurerAdapter() {
					});
			webSecurity.apply(adapter);
		}
		return webSecurity.build();
	}
}

2. springSecurityFilterChain 初始化

待補充

3. springSecurityFilterChain 如何被新增到Filter

建立一個 targetBeanName 屬性值為 springSecurityFilterChain 的 DelegatingFilterProxy Filter 例項, 並註冊到 ServletContext

1. AbstractSecurityWebApplicationInitializer

public abstract class AbstractSecurityWebApplicationInitializer
		implements WebApplicationInitializer {
			
	public static final String DEFAULT_FILTER_NAME = "springSecurityFilterChain";
	
	public final void onStartup(ServletContext servletContext) {
		beforeSpringSecurityFilterChain(servletContext);
		if (this.configurationClasses != null) {
			AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
			rootAppContext.register(this.configurationClasses);
			servletContext.addListener(new ContextLoaderListener(rootAppContext));
		}
		if (enableHttpSessionEventPublisher()) {
			servletContext.addListener(
					"org.springframework.security.web.session.HttpSessionEventPublisher");
		}
		servletContext.setSessionTrackingModes(getSessionTrackingModes());
		insertSpringSecurityFilterChain(servletContext);
		afterSpringSecurityFilterChain(servletContext);
	}
	
	 /**
	  * 建立一個 DelegatingFilterProxy 例項註冊 Filter
	  */
	private void insertSpringSecurityFilterChain(ServletContext servletContext) {
		String filterName = DEFAULT_FILTER_NAME;
		DelegatingFilterProxy springSecurityFilterChain = new DelegatingFilterProxy(
				filterName);
		String contextAttribute = getWebApplicationContextAttribute();
		if (contextAttribute != null) {
			springSecurityFilterChain.setContextAttribute(contextAttribute);
		}
		registerFilter(servletContext, true, filterName, springSecurityFilterChain);
	}
	
	 /**
	  * 將 DelegatingFilterProxy 新增到 ServletContext 的 Filter中
	  */
	private void registerFilter(ServletContext servletContext,
								boolean insertBeforeOtherFilters, String filterName, Filter filter) {
		Dynamic registration = servletContext.addFilter(filterName, filter);
		if (registration == null) {
			throw new IllegalStateException(
					"Duplicate Filter registration for '" + filterName
							+ "'. Check to ensure the Filter is only configured once.");
		}
		registration.setAsyncSupported(isAsyncSecuritySupported());
		EnumSet<DispatcherType> dispatcherTypes = getSecurityDispatcherTypes();
		registration.addMappingForUrlPatterns(dispatcherTypes, !insertBeforeOtherFilters,
				"/*");
	}
}

4. springSecurityFilterChain 如何工作

DelegatingFilterProxy 在請求第一次到來時初始化, 從應用中獲取bean名稱為 springSecurityFilterChain 的例項, 然後呼叫 springSecurityFilterChain 的 doFilter 方法

public class DelegatingFilterProxy extends GenericFilterBean {


	@Nullable
	private String targetBeanName;

	@Nullable
	private volatile Filter delegate;
	
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		// 懶載入,使用時進行初始化
		Filter delegateToUse = this.delegate;
		if (delegateToUse == null) {
			// 初始化 delegate
			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;
			}
		}
		 // 呼叫 springSecurityFilterChain 的 doFilter()
		invokeDelegate(delegateToUse, request, response, filterChain);
	}
	
	protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
		String targetBeanName = getTargetBeanName();
		Assert.state(targetBeanName != null, "No target bean name set");
		// 根據 bean 名稱獲取例項物件
		Filter delegate = wac.getBean(targetBeanName, Filter.class);
		if (isTargetFilterLifecycle()) {
			delegate.init(getFilterConfig());
		}
		return delegate;
	}
}