过滤器可以简单理解成用于拦截请求,并执行相应逻辑的代码。
在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可能会调用同一个过滤器多次。