過濾器可以簡單理解成用於攔截請求,並執行相應邏輯的程式碼。
在Spring Security架構中實現過濾器
在SpringSecurity中,可以通過實現 javax.servlet 包中的 Filter介面構造過濾器。
我們通過實現Filter 介面的doFilter() 方法,執行相關邏輯。該方法包含三個引數:
- ServletRequest:表示http請求,可用它獲得請求相關資訊。
- ServletResponse:表示http響應,可向它新增相關資訊,最後傳回客戶端。
- FilterChain:表示過濾鏈,用於把請求和響應轉發到過濾鏈上的下一個過濾器。
Spring Security為我們提供了一些過濾器實現,例如:
- BasicAuthenticationFilter:用於http認證。
- CsrfFilter:用於跨請求保護。
- CorsFilter:負責跨域資源共享 (CORS) 授權規則。
多個過濾器集合在一起形成一條過濾鏈,它們之間有一定順序。你可以通過存在於過濾鏈上的一個過濾器,在它的相對位置新增一個新的過濾器。
在過濾鏈上的一個過濾器前面,新增一個新的過濾器
可以通過下面的方法在某過濾器前面新增一個新的過濾器。
@Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new CustomFilter(), BasicAuthenticationFilter.class);
}
}
CustomFilter是你自定義實現的過濾器類,BasicAuthenticationFilter是認證過濾器的預設型別。
在過濾鏈上的一個過濾器後面,新增一個新的過濾器
下面的程式碼是在某過濾器後面新增一個新的過濾器。
@Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterAfter(new CustomFilter(), BasicAuthenticationFilter.class);
}
}
在過濾鏈上的一個過濾器位置,新增一個新的過濾器
下面程式碼是在一個過濾器的位置上,新增一個新的過濾器。
@Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http..addFilterAt(new CustomFilter(), BasicAuthenticationFilter.class);
}
}
當在一個過濾器的位置上,加入一個新的過濾器,Spring Security不會假設該位置上只有一個過濾器, 它也不會保證該位置上過濾器的順序。
Spring Security 提供的過濾器實現
Spring Security 提供了一些實現Filter藉口的抽象類,並在裡面加入一些功能。你可以通過繼承這些類來構建過濾器類。例如OncePerRequestFilter等。由於Spring Security不能保證一個過濾器對於同一個請求不會被呼叫多次,我們可以是過濾器繼承OncePerRequestFilter來保證。
演示程式碼
在過濾器BasicAuthenticationFilter的前面、當前位置、後面各新增一個新的過濾器。
建立一個SpringBoot專案,它的依賴如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
建立三個過濾器,分別為BeforeFilter、CurrentFilter、AfterFilter。
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class BeforeFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("BeforeFilter: " + "在其前面插入的過濾器");
chain.doFilter(request, response);
}
}
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class CurrentFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("CurrentFilter: " + "在其當前位置插入的過濾器");
chain.doFilter(request, response);
}
}
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class AfterFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("AfterFilter: " + "在其後面插入的過濾器");
chain.doFilter(request, response);
}
}
建立一個配置類ProjectConfig
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import hookind.security009.filter.AfterFilter;
import hookind.security009.filter.BeforeFilter;
import hookind.security009.filter.CurrentFilter;
@Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().permitAll();
// 在前面加入一個過濾器
http.addFilterBefore(new BeforeFilter(), BasicAuthenticationFilter.class);
// 在當前位置加入一個過濾器
http.addFilterAt(new CurrentFilter(), BasicAuthenticationFilter.class);
// 在其後面加入一個過濾器
http.addFilterAfter(new AfterFilter(), BasicAuthenticationFilter.class);
}
}
建立一個Controller類
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(){
return "Hello";
}
}
啟動程式後,在瀏覽器輸入網址 http://localhost:8080/hello
,我的控制檯輸出如下:
BeforeFilter: 在其前面插入的過濾器
CurrentFilter: 在其當前位置插入的過濾器
AfterFilter: 在其後面插入的過濾器
BeforeFilter: 在其前面插入的過濾器
CurrentFilter: 在其當前位置插入的過濾器
AfterFilter: 在其後面插入的過濾器
BeforeFilter: 在其前面插入的過濾器
CurrentFilter: 在其當前位置插入的過濾器
AfterFilter: 在其後面插入的過濾器
由上可知,過濾器是按BeforeFilter、CurrentFilter、AfterFilter順序呼叫的。也表示,對於同一個請求,Spring Security可能會呼叫同一個過濾器多次。