Spring Security 原始碼學習(二): Spring Security自動配置(初始化流程)
阿新 • • 發佈:2022-05-18
【深度好文】: 「和耳朵」SpringSecurity是如何代理過濾器鏈的?
1. 自動配置security的bean資訊
下面是 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; } }