Spring Security Filter 学习笔记

过滤器可以简单理解成用于拦截请求,并执行相应逻辑的代码。

在Spring Security架构中实现过滤器

在SpringSecurity中,可以通过实现 javax.servlet 包中的 Filter接口构造过滤器。

我们通过实现Filter 接口的doFilter() 方法,执行相关逻辑。该方法包含三个参数:

  1. ServletRequest:表示http请求,可用它获得请求相关信息。
  2. ServletResponse:表示http响应,可向它添加相关信息,最后传回客户端。
  3. 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可能会调用同一个过滤器多次。

上一篇:RH_Window开源项目


下一篇:第十次作业