天天看點

如何檢視Spring Security中的過濾器鍊?

作者:軟體架構

保護 Spring Boot 應用程式的第一步是将 spring-boot-starter-security 依賴項添加到建構中。在項目的 pom.xml 檔案中,添加以下依賴項:

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-security</artifactId>
</dependency>           

上面的依賴項是保護應用程式所需的唯一東西。當應用程式啟動時,自動配置将檢測類路徑中的 Spring Security包,并設定一些基本的安全性配置。

如何檢視Spring Security中的過濾器鍊?

一、輸出預設過濾器鍊

Spring Security 本質就是基于過濾器鍊實作的,每一個接口請求都會按順序經過這些過濾器的“過濾”,每個過濾器承擔的各自的職責,組合起來共同完成認證和鑒權。

Spring Security會自動建立一個名為 springSecurityFilterChain 的過濾器鍊,并注入到Spring 容器中,這個過濾器将負責所有的安全管理,包括使用者的認證、授權、重定向到登入頁面等。

如何檢視Spring Security中的過濾器鍊?

通過 FilterChainProxy 來統一管理 Spring Security Filter,FilterChainProxy 本身則通過 Spring 提供的 DelegatingFilterProxy 代理過濾器嵌入到 Web Filter 之中。

可以簡單修改一下Spring Boot啟動類,如下所示:

@SpringBootApplication
public class FirstApp
{
 public static void main( String[] args )
 {
 ConfigurableApplicationContext context = SpringApplication.run(FirstApp.class, args);
 FilterChainProxy proxy = (FilterChainProxy) context.getBean("springSecurityFilterChain");
 if (proxy != null) {
 List<Filter> filters = proxy.getFilterChains().get(0).getFilters();
 filters.forEach(x-> System.out.println(x));
 }
 System.out.println("hello spring security");
 }
}           

就可以列印輸出Spring Security中的預設過濾器鍊,如下所示:

org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@67dba613
org.springframework.security.web.context.SecurityContextPersistenceFilter@79a1728c
org.springframework.security.web.header.HeaderWriterFilter@71391b3f
org.springframework.security.web.csrf.CsrfFilter@5d235104
org.springframework.security.web.authentication.logout.LogoutFilter@44c5a16f
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@29fc1a2b
org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@66596a88
org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@57540fd0
org.springframework.security.web.authentication.www.BasicAuthenticationFilter@677b8e13
org.springframework.security.web.savedrequest.RequestCacheAwareFilter@41f35f7c
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@3013909b
org.springframework.security.web.authentication.AnonymousAuthenticationFilter@5cf8edcf
org.springframework.security.web.session.SessionManagementFilter@12db3386
org.springframework.security.web.access.ExceptionTranslationFilter@6fca2a8f
org.springframework.security.web.access.intercept.FilterSecurityInterceptor@3a4ba480           

上述預設過濾器鍊的過濾器,和 WebSecurityConfigurerAdapter 安全配置類的兩段代碼有關系。

1. configure(HttpSecurity http) 方法

在configure() 方法中,預設開啟了 formLogin 表單認證和 httpBasic() 基礎認證。

protected void configure(HttpSecurity http) throws Exception {
 logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");

 http
 .authorizeRequests()
 .anyRequest().authenticated()
 .and()
 .formLogin().and()
 .httpBasic();
 }           

2. getHttp() 方法

在 getHttp() 方法中,預設開啟了 csrf、exceptionHandling、headers .... 等等好的過濾器,具體檢視下面的代碼。

protected final HttpSecurity getHttp() throws Exception {
 if (http != null) {
 return http;
 }

 AuthenticationEventPublisher eventPublisher = getAuthenticationEventPublisher();
 localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);

 AuthenticationManager authenticationManager = authenticationManager();
 authenticationBuilder.parentAuthenticationManager(authenticationManager);
 Map<Class<?>, Object> sharedObjects = createSharedObjects();

 http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
 sharedObjects);
 if (!disableDefaults) {
 // @formatter:off
 http
 .csrf().and()
 .addFilter(new WebAsyncManagerIntegrationFilter())
 .exceptionHandling().and()
 .headers().and()
 .sessionManagement().and()
 .securityContext().and()
 .requestCache().and()
 .anonymous().and()
 .servletApi().and()
 .apply(new DefaultLoginPageConfigurer<>()).and()
 .logout();
 // @formatter:on
 ClassLoader classLoader = this.context.getClassLoader();
 List<AbstractHttpConfigurer> defaultHttpConfigurers =
 SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);

 for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
 http.apply(configurer);
 }
 }
 configure(http);
 return http;
 }           

上述 getHttp() 方法是在下面的 init() 方法中被調用的,init() 方法的代碼如下所示:

public void init(final WebSecurity web) throws Exception {
 final HttpSecurity http = getHttp();
 web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
 FilterSecurityInterceptor securityInterceptor = http
 .getSharedObject(FilterSecurityInterceptor.class);
 web.securityInterceptor(securityInterceptor);
 });
 }           

同時,在該方法中也添加了 FilterSecurityInterceptor 過濾器。

現在,應該就了解了前面代碼中預設輸出的過濾器中的過濾器清單了。

二、清理過濾器鍊中全部過濾器

那麼有沒有将過濾器鍊中的所有過濾器全部清理掉呢?

可以 ... 隻需要建立一個 SecurityConfig 安全配置類,繼承 WebSecurityConfigurerAdapter 類。然後重寫其中的如下方法:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
 protected SecurityConfig() {
 super(true);
 }

 @Override
 protected void configure(HttpSecurity http) throws Exception {

 }
}           

這樣,Spring Security 中的預設過濾器鍊中的過濾器就全部去除了。

如何檢視Spring Security中的過濾器鍊?

三、DefaultSecurityFilterChain

SecurityFilterChain 就是我們平時所說的 Spring Security 中的過濾器鍊。

如何檢視Spring Security中的過濾器鍊?

它裡邊定義了兩個方法,一個是 matches 方法用來比對請求,另外一個 getFilters 方法傳回一個 List 集合,集合中放着 Filter 對象。

當一個請求到來時,用 matches 方法去比較請求是否和目前鍊吻合,如果吻合,就傳回 getFilters 方法中的過濾器,那麼目前請求會逐個經過 List 集合中的過濾器。

SecurityFilterChain 接口隻有一個實作類,那就是 DefaultSecurityFilterChain,如圖所示:

如何檢視Spring Security中的過濾器鍊?

DefaultSecurityFilterChain實作了SecurityFilterChain接口。

DefaultSecurityFilterChain 其實就相當于是 Spring Security 中的過濾器鍊,一個 DefaultSecurityFilterChain 代表一個過濾器鍊,如果系統中存在多個過濾器鍊,則會存在多個 DefaultSecurityFilterChain 對象。

FilterChainProxy 中可以存在多個過濾器鍊(DefaultSecurityFilterChain),如圖所示:

如何檢視Spring Security中的過濾器鍊?

前面我們有一段輸出預設過濾器的代碼,就是擷取Spring Security 中的第一個 DefaultSecurityFilterChain 對象:

List<Filter> filters = proxy.getFilterChains().get(0).getFilters();

可以看到,當請求到達 FilterChainProxy 之後,FilterChainProxy 會根據請求的路徑,将請求轉發到不同的 Spring Security 過濾器鍊上面去,不同的 Spring Security Filters(過濾器鍊) 對應了不同的過濾器,也就是不同的請求将經過不同的過濾器。

如何檢視Spring Security中的過濾器鍊?