欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring Security基本架構(gòu)與初始化操作流程詳解

 更新時間:2023年03月28日 10:42:01   作者:T.Y.Bao  
這篇文章主要介紹了Spring Security基本架構(gòu)與初始化操作流程,Spring Security是一個能夠為基于Spring的企業(yè)應(yīng)用系統(tǒng)提供聲明式的安全訪問控制解決方案的安全框架

Spring Security 是基于web的安全組件,所以一些相關(guān)類會分散在 spring-security包和web包中。Spring Security通過自定義Servlet的Filter的方式實現(xiàn),具體架構(gòu)可參考官網(wǎng)Spring Security: Architecture

這里使用Spring Boot 2.7.4版本,對應(yīng)Spring Security 5.7.3版本

基本架構(gòu)

首先左側(cè)是Servlet中的Filter組成的FilterChain,Spring Security通過注冊一個DelegatingFilterProxy的Filter,然后在該Proxy中內(nèi)置多條Spring Security組織的Security Filter Chain(chain中套娃一個chain),一個Security Filter Chain又有多個Filter,通過不同的規(guī)則將Request匹配到第一個滿足條件的Security Filter Chain。

Web源碼

既然Spring Security涉及到Filter,而Filter是Servlet中的組件,這里就存在一個將Spring Security的頂級Filter注冊到Servlet Context的過程。

首先關(guān)注javax.servlet.ServletContainerInitializer,該類是tomcat-embed-core包中的類:

// 通過SPI方式導入實現(xiàn)類:
// META-INF/services/javax.servlet.ServletContainerInitializer 
public interface ServletContainerInitializer {
	/** 
	* Receives notification during startup of a web application of the classes within the web application 
	* that matched the criteria defined via the annotation:
	* javax.servlet.annotation.HandlesTypes 
	* 
	* 處理javax.servlet.annotation.HandlesTypes注解標注類型的實現(xiàn)類
	**/
    void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
}

該接口實現(xiàn)類由SPI方式導入,我們來到spring-web包中:

可以看到spring對 該接口的實現(xiàn)類為:org.springframework.web.SpringServletContainerInitializer

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
	@Override
	public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
			throws ServletException {
		List<WebApplicationInitializer> initializers = Collections.emptyList();
		...
		// 添加
		if (webAppInitializerClasses != null) {
			initializers = new ArrayList<>(webAppInitializerClasses.size());
			for (Class<?> waiClass : webAppInitializerClasses) {
						initializers.add((WebApplicationInitializer)
								ReflectionUtils.accessibleConstructor(waiClass).newInstance());
			}
		}
		...
		// 排序
		AnnotationAwareOrderComparator.sort(initializers);
		// 執(zhí)行
		for (WebApplicationInitializer initializer : initializers) {
			initializer.onStartup(servletContext);
		}
	}
}

SpringServletContainerInitializer中調(diào)用了一系列org.springframework.web.WebApplicationInitializer#onStartup

可以看到WebApplicationInitializer 有一系列實現(xiàn)類:

其中就有Security相關(guān)的。到此,以上均為 Spring Web中的內(nèi)容,Spring Security就是基于以上擴展而來。

接上文,來看看org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer:

public abstract class AbstractSecurityWebApplicationInitializer implements WebApplicationInitializer {
	public static final String DEFAULT_FILTER_NAME = "springSecurityFilterChain";
	...
	@Override
	public final void onStartup(ServletContext servletContext) {
		beforeSpringSecurityFilterChain(servletContext);
		...
		insertSpringSecurityFilterChain(servletContext);
		afterSpringSecurityFilterChain(servletContext);
	}
	...
}

但是,經(jīng)過調(diào)試發(fā)現(xiàn),Spring Security的Filter注冊過程并不是上面的步驟。

重要:

Spring Security 注冊Filter 不是通過上文的 javax.servlet.ServletContainerInitializerorg.springframework.web.WebApplicationInitializer#onStartup 而是org.springframework.boot.web.servlet.ServletContextInitializer,來看看ServletContextInitializer的說明:

/**
 * 不同于WebApplicationInitializer,實現(xiàn)該接口的類(且沒有實現(xiàn)WebApplicationInitializer)
 * 不會被SpringServletContainerInitializer檢測到,所以不會由servlet容器自動啟動。
 * 該類的目的和ServletContainerInitializer一樣,但是 其中的Servlet的生命周期由Spring控制而不是Servlet容器。
 */
@FunctionalInterface
public interface ServletContextInitializer {
	void onStartup(ServletContext servletContext) throws ServletException;
}

DelegatingFilterProxy

首先來看自動配置類:org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration

@AutoConfiguration(after = SecurityAutoConfiguration.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(SecurityProperties.class)
@ConditionalOnClass({ AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class })
public class SecurityFilterAutoConfiguration {
	// DEFAULT_FILETER_NAME = "springSecurityFilterChain"
	private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;
	// 必須存在名稱為springSecurityFilterChain的bean
	// 名稱為springSecurityFilterChain的bean實際上類型即是 org.springframework.security.web.FilterChainProxy
	@Bean
	@ConditionalOnBean(name = DEFAULT_FILTER_NAME)
	public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
			SecurityProperties securityProperties) {
		DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
				DEFAULT_FILTER_NAME);
		registration.setOrder(securityProperties.getFilter().getOrder());
		registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
		return registration;
	}
	...
}

可以看到DelegatingFilterProxyRegistrationBean被注入Bean容器,且名稱為"springSecurityFilterChain"的Bean必須存在,而DelegatingFilterProxyRegistrationBean#getFilter用來獲取真正的Security Filter代理類DelegatingFilterProxy,需要注意的是,DelegatingFilterProxy實現(xiàn)了Filter接口。

先來看看DelegatingFilterProxyRegistrationBean的類圖結(jié)構(gòu):

DelegatingFilterProxyRegistrationBean負責整合Servlet Filter注冊(主要就是代理類注冊)和Spring生命周期,而真正的代理類DelegatingFilterProxy通過

DelegatingFilterProxyRegistrationBean#getFilter獲取。這體現(xiàn)了職責單一的設(shè)計原則。

public class DelegatingFilterProxyRegistrationBean ... {
	...
	@Override
	public DelegatingFilterProxy getFilter() {
		// 創(chuàng)建真正的代理(匿名子類),并具有延遲加載的能力
		return new DelegatingFilterProxy(this.targetBeanName, getWebApplicationContext()) {
			@Override
			protected void initFilterBean() throws ServletException {
				// Don't initialize filter bean on init()
			}
		};
	}
	...
}

接下來,DelegatingFilterProxyRegistrationBean中的DelegatingFilterProxy需要完成對多個SecurityFilterChain的代理。而這個代理過程Security又通過一個代理類org.springframework.security.web.FilterChainProxy完成 。意思是,DelegatingFilterProxy是整個Security的代理,而FilterChainProxy是SecurityFilterChain的代理,且DelegatingFilterProxy是通過FilterChainProxy來完成代理的(代理一個代理)。

來看看DelegatingFilterProxy

public class DelegatingFilterProxy extends GenericFilterBean {
	// 就是 springSecurityFilterChain,代表FilterChainProxy的beanName
	@Nullable
	private String targetBeanName;
	// 代理的FilterChainProxy
	@Nullable
	private volatile Filter delegate;
	...
	@Override
	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) {
					...
					// 初始化代理類
					delegateToUse = initDelegate(wac);
				}
				this.delegate = delegateToUse;
			}
		}
		// Let the delegate perform the actual doFilter operation.
		invokeDelegate(delegateToUse, request, response, filterChain);
	}
	...
	protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
		String targetBeanName = getTargetBeanName();
		// 容器中獲取名稱為springSecurityFilterChain 類型為Filter的bean
		// 即 FilterChainProxy
		// 所以 注冊 DelegatingFilterProxyRegistrationBean 時必須有 @ConditionalOnBean(name="springSecurityFilterChain")
		Filter delegate = wac.getBean(targetBeanName, Filter.class);
		...
		return delegate;
	}
}

上文說到,在注冊DelegatingFilterProxyRegistrationBean的自動配置類中 必須要有springSecurityFilterChain名稱的bean存在,而這個名稱為springSecurityFilterChain的bean實際上類型即是 org.springframework.security.web.FilterChainProxy。

整個流程如下:

有點像 道生一,一生二,二生三,三生萬物 的思想,我將它命名為 道德經(jīng)設(shè)計模式,嘿嘿 。

那么FilterChainProxy又是在哪兒注入的呢?

FilterChainProxy

在配置類org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration中我們可以發(fā)現(xiàn),這里注入了FilterChainProxy

@Configuration(proxyBeanMethods = false)
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
	...
	private WebSecurity webSecurity;
	// 多個SecurityFilterChain
	private List<SecurityFilterChain> securityFilterChains = Collections.emptyList();
	// 多個WebSecurityCustomizer
	private List<WebSecurityCustomizer> webSecurityCustomizers = Collections.emptyList();
	...
	// 注入一個Filter,指定名稱為springSecurityFilterChain
	@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	public Filter springSecurityFilterChain() throws Exception {
		...
		for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {
			this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
			// 為每個SecurityFilterChain中的每個Filter添加攔截方法
			for (Filter filter : securityFilterChain.getFilters()) {
				if (filter instanceof FilterSecurityInterceptor) {
					this.webSecurity.securityInterceptor((FilterSecurityInterceptor) filter);
					break;
				}
			}
		}
		// 自定義器對每個SecurityFilterChain均生效
		for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {
			customizer.customize(this.webSecurity);
		}
		// 這里build()方法返回  org.springframework.security.web.FilterChainProxy
		return this.webSecurity.build();
	}
	...
	// 自動注入, 通常我們需要自定義的就是這個SecurityFilterChain類型
	// 只需要在業(yè)務(wù)配置類中注冊一個SecurityFilterChain類型的bean就能被注入到這里
	@Autowired(required = false)
	void setFilterChains(List<SecurityFilterChain> securityFilterChains) {
		this.securityFilterChains = securityFilterChains;
	}
	// 自動注入
	@Autowired(required = false)
	void setWebSecurityCustomizers(List<WebSecurityCustomizer> webSecurityCustomizers) {
		this.webSecurityCustomizers = webSecurityCustomizers;
	}
}

在業(yè)務(wù)配置類中,我們可以自定義SecurityFilterChainWebSecurityCustomizer的bean,配置如下:

@Configuration
public class SecurityConfig {
	@Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf().disable();
        // 必須顯式注明,配合CorsConfigurationSource的Bean,不然即使在web里面配置了跨域,security這里依然會cors error
        http.cors();
        http.authorizeRequests()
                .antMatchers(AUTH_WHITELIST).permitAll()
                .anyRequest().authenticated();
        http.formLogin().successHandler(loginSuccessHandler);
        http.oauth2Login().successHandler(giteeSuccessHandler);
        http.exceptionHandling().accessDeniedHandler(restAccessDeniedHandler);
        http.addFilterBefore(bearAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }
	@Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring().antMatchers("/ignore1", "/ignore2");
    }
}

OK,我們再來看看 org.springframework.security.web.FilterChainProxy:

public class FilterChainProxy extends GenericFilterBean {
	private List<SecurityFilterChain> filterChains;
	private HttpFirewall firewall = new StrictHttpFirewall();
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		...
		doFilterInternal(request, response, chain);
		...
	}
	private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		// 轉(zhuǎn)化為org.springframework.security.web.firewall.FirewalledRequest
		// reject potentially dangerous requests and/or wrap them to control their behaviour.
		FirewalledRequest firewallRequest = this.firewall.getFirewalledRequest((HttpServletRequest) request);
		HttpServletResponse firewallResponse = this.firewall.getFirewalledResponse((HttpServletResponse) response);
		// #getFilters會在所有SecurityFilterChain中進行匹配
		List<Filter> filters = getFilters(firewallRequest);
		...
		// 轉(zhuǎn)化為 VirtualFilterChain
		// VirtualFilterChain是FilterChainProxy內(nèi)部靜態(tài)類
		VirtualFilterChain virtualFilterChain = new VirtualFilterChain(firewallRequest, chain, filters);
		// 開啟 SecurityFilterChain中所有filter過程
		virtualFilterChain.doFilter(firewallRequest, firewallResponse);
	}
	private List<Filter> getFilters(HttpServletRequest request) {
		for (SecurityFilterChain chain : this.filterChains) {
			// 返回第一個符合規(guī)則的SecurityFilterChain
			if (chain.matches(request)) {
				return chain.getFilters();
			}
		}
		return null;
	}
	/**
	 * 執(zhí)行額外的 filters,控制filters執(zhí)行過程
	 * Internal {@code FilterChain} implementation that is used to pass a request through
	 * the additional internal list of filters which match the request.
	 */
	private static final class VirtualFilterChain implements FilterChain {
		...
		private final FilterChain originalChain;
		private final List<Filter> additionalFilters;
		private final FirewalledRequest firewalledRequest;
		// 該SecurityFilterChain中所有filter的數(shù)量
		private final int size;
		// 當前filter的位置
		private int currentPosition = 0;
		...
		@Override
		public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
			if (this.currentPosition == this.size) {
				// 執(zhí)行完畢
				// Deactivate path stripping as we exit the security filter chain
				this.firewalledRequest.reset();
				this.originalChain.doFilter(request, response);
				return;
			}
			// 繼續(xù)執(zhí)行filterChain中下一個filter
			this.currentPosition++;
			Filter nextFilter = this.additionalFilters.get(this.currentPosition - 1);
			nextFilter.doFilter(request, response, this);
		}
		...
	}
	...
}

Filters

按順序排序,Spring Security內(nèi)置了以下Filter:

  • ForceEagerSessionCreationFilter
  • ChannelProcessingFilter
  • WebAsyncManagerIntegrationFilter
  • SecurityContextPersistenceFilter
  • HeaderWriterFilter
  • CorsFilter
  • CsrfFilter
  • LogoutFilter
  • OAuth2AuthorizationRequestRedirectFilter
  • Saml2WebSsoAuthenticationRequestFilter
  • X509AuthenticationFilter
  • AbstractPreAuthenticatedProcessingFilter
  • CasAuthenticationFilter
  • OAuth2LoginAuthenticationFilter
  • Saml2WebSsoAuthenticationFilter
  • UsernamePasswordAuthenticationFilter
  • DefaultLoginPageGeneratingFilter
  • DefaultLogoutPageGeneratingFilter
  • ConcurrentSessionFilter
  • DigestAuthenticationFilter
  • BearerTokenAuthenticationFilter
  • BasicAuthenticationFilter
  • RequestCacheAwareFilter
  • SecurityContextHolderAwareRequestFilter
  • JaasApiIntegrationFilter
  • RememberMeAuthenticationFilter
  • AnonymousAuthenticationFilter
  • OAuth2AuthorizationCodeGrantFilter
  • SessionManagementFilter
  • ExceptionTranslationFilter : allows translation of AccessDeniedException and
  • AuthenticationException into HTTP responses
  • FilterSecurityInterceptor (新版本由 AuthorizationFilter 取代,該Interceptor即是做鑒權(quán)的)
  • SwitchUserFilter

到此這篇關(guān)于Spring Security基本架構(gòu)與初始化操作流程詳解的文章就介紹到這了,更多相關(guān)Spring Security基本架構(gòu)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java ArrayAdapter用法案例詳解

    Java ArrayAdapter用法案例詳解

    這篇文章主要介紹了Java ArrayAdapter用法案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • 詳解springmvc如何處理接受http請求

    詳解springmvc如何處理接受http請求

    這篇文章主要給大家介紹了springmvc如何處理接受http請求,文中通過代碼示例給大家講解的非常詳細,對大家的學習或工作有一定的幫助,需要的朋友可以參考下
    2024-02-02
  • SpringBoot集成Sharding Jdbc使用復合分片的實踐

    SpringBoot集成Sharding Jdbc使用復合分片的實踐

    數(shù)據(jù)庫分庫分表中間件是采用的 apache sharding。本文主要介紹了SpringBoot集成Sharding Jdbc使用復合分片的實踐,具有一定的參考價值,感興趣的可以了解一下
    2021-09-09
  • 關(guān)于解決雪花算法生成的ID傳輸前端后精度丟失問題

    關(guān)于解決雪花算法生成的ID傳輸前端后精度丟失問題

    這篇文章主要介紹了關(guān)于解決雪花算法生成的ID傳輸前端后精度丟失問題,雪花算法生成的ID傳輸?shù)角岸藭r,會出現(xiàn)后三位精度丟失,本文提供了解決思路,需要的朋友可以參考下
    2023-03-03
  • SpringBoot使用RabbitMQ延時隊列(小白必備)

    SpringBoot使用RabbitMQ延時隊列(小白必備)

    這篇文章主要介紹了SpringBoot使用RabbitMQ延時隊列(小白必備),詳細的介紹延遲隊列的使用場景及其如何使用,需要的小伙伴可以一起來了解一下
    2019-12-12
  • Java 關(guān)系運算符詳情及案例(上)

    Java 關(guān)系運算符詳情及案例(上)

    這篇文章主要介紹了Java 關(guān)系運算符詳情及案例實現(xiàn),Java 也提供了許多類型的運算符,可以根據(jù)需要使用它們來執(zhí)行各種計算和函數(shù),包括邏輯、算術(shù)、關(guān)系等。它們根據(jù)它們提供的功能進行分類,下面將詳細介紹該內(nèi)容,需要的朋友可以參考一下
    2021-12-12
  • Mybatis-Plus使用saveOrUpdate及問題解決方法

    Mybatis-Plus使用saveOrUpdate及問題解決方法

    本文主要介紹了Mybatis-Plus使用saveOrUpdate及問題解決方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-01-01
  • Spring cache整合redis代碼實例

    Spring cache整合redis代碼實例

    這篇文章主要介紹了Spring cache整合redis代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-04-04
  • Win11系統(tǒng)下載安裝java的詳細過程

    Win11系統(tǒng)下載安裝java的詳細過程

    這篇文章主要介紹了Win11如何下載安裝java,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-05-05
  • 創(chuàng)建一個Java的不可變對象

    創(chuàng)建一個Java的不可變對象

    這篇文章主要介紹了創(chuàng)建一個Java的不可變對象,一個類的對象在通過構(gòu)造方法創(chuàng)建后如果狀態(tài)不會再被改變,那么它就是一個不可變(immutable)類。它的所有成員變量的賦值僅在構(gòu)造方法中完成,不會提供任何 setter 方法供外部類去修改,需要的朋友可以參考下
    2021-11-11

最新評論