spring-security原理与应用系列:核心过滤器

目录

运行机制

WebSecurity

SecurityFilterChain

SecurityFilterChains

FilterChainProxy

VirtualFilterChain

内部结构

类图

FilterChainProxy

FilterChain

VirtualFilterChain

小结


紧接上一篇文章,这一篇我们来看看FilterChainProxy类的运行机制及内部结构。

FilterChainProxy是spring-security框架的核心过滤器,服务启动过程中,系统会根据我们自定义的配置类内容生成FilterChainProxy的对象;服务接收到HTTP请求后,就会将请求转发到该过滤器对象进行处理。

运行机制

前面的篇章里,我们看到FilterChainProxy对象是建造者WebSecurity在构建步骤的最后一步里创建的。

找到WebSecurity的performBuild()方法,如下所示:

WebSecurity

java 复制代码
@Override
protected Filter performBuild() throws Exception {
   List<SecurityFilterChain> securityFilterChains = new ArrayList<SecurityFilterChain>(
         chainSize);
   for (RequestMatcher ignoredRequest : ignoredRequests) {
      securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
   }
   for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
      securityFilterChains.add(securityFilterChainBuilder.build());
   }
   FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
   if (httpFirewall != null) {
      filterChainProxy.setFirewall(httpFirewall);
   }
   filterChainProxy.afterPropertiesSet();
   Filter result = filterChainProxy;
   postBuildAction.run();
   return result;
}

在这里,创建FilterChainProxy对象时,输入了类型为List<SecurityFilterChain>的构造参数securityFilterChains对象,这个参数对象是如何进行赋值的,我们后续探究。我们先来看看SecurityFilterChain这个类。

点击SecurityFilterChain,如下所示:

SecurityFilterChain

public interface SecurityFilterChain {

boolean matches(HttpServletRequest request);

List<Filter> getFilters();

}

在这里,大概可以猜测,SecurityFilterChain的对象会保存待过滤的请求地址及对应的Filter对象。matches()方法用来判断用户请求与配置的请求地址是否匹配,getFilters()方法用来返回对应的过滤器对象。

我们先运行一下程序看看。

假设自定义配置如下:

S ecurityFilterChains

@EnableWebSecurity

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Override

public void configure(WebSecurity web) {

web.ignoring()

.antMatchers("/css/**", "/js/**")

.regexMatchers("/api/public/.*");

}

}

在这里,先进行简单的配置,即设置"/css/**"、 "/js/**"和"/api/public/.*"为不需要过滤的请求地址。

设置断点,如下所示:

这里,可以看到每定义一个请求地址,就会有一个DefaultSecurityFilterChain对象。接下来看看FilterChainProxy对象是如何执行请求过滤的。

点击FilterChainProxy,如下所示:

FilterChainProxy

由于FilterChainProxy实现了Filter类,所以也是一个过滤器,过滤器一般都是执行doFilter()方法,如下所示:

public void doFilter(ServletRequest request, ServletResponse response,

FilterChain chain) throws IOException, ServletException {

boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;

if (clearContext) {

try {

request.setAttribute(FILTER_APPLIED, Boolean.TRUE);

doFilterInternal(request, response, chain);

} finally {

SecurityContextHolder.clearContext();

request.removeAttribute(FILTER_APPLIED);

}

} else {

doFilterInternal(request, response, chain);

}

}

点击doFilterInternal,如下所示:

private void doFilterInternal(ServletRequest request, ServletResponse response,

FilterChain chain) throws IOException, ServletException {

FirewalledRequest fwRequest = firewall

.getFirewalledRequest((HttpServletRequest) request);

HttpServletResponse fwResponse = firewall

.getFirewalledResponse((HttpServletResponse) response);

List<Filter> filters = getFilters(fwRequest);

if (filters == null || filters.size() == 0) {

fwRequest.reset();

chain.doFilter(fwRequest, fwResponse);

return;

}

VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);

vfc.doFilter(fwRequest, fwResponse);

}

点击getFilters(),如下所示:

private List<Filter> getFilters(HttpServletRequest request) {

for (SecurityFilterChain chain : filterChains) {

if (chain.matches(request)) {

return chain.getFilters();

}

}

return null;

}

在这里,将request与SecurityFilterChain 进行匹配,只要匹配到一个就返回这个SecurityFilterChain下的Filters,后续的SecurityFilterChain 会被忽略。

接下来将Filters交由VirtualFilterChain进行处理,如下所示:

VirtualFilterChain

java 复制代码
private static class VirtualFilterChain implements FilterChain {
   private final FilterChain originalChain;
   private final List<Filter> additionalFilters;
   private final FirewalledRequest firewalledRequest;
   private final int size;
   private int currentPosition = 0;
   private VirtualFilterChain(FirewalledRequest firewalledRequest,
         FilterChain chain, List<Filter> additionalFilters) {
      this.originalChain = chain;
      this.additionalFilters = additionalFilters;
      this.size = additionalFilters.size();
      this.firewalledRequest = firewalledRequest;
   }
   public void doFilter(ServletRequest request, ServletResponse response)
         throws IOException, ServletException {
      if (currentPosition == size) {
         this.firewalledRequest.reset();
         originalChain.doFilter(request, response);
      } else {
         currentPosition++;
         Filter nextFilter = additionalFilters.get(currentPosition - 1);
         nextFilter.doFilter(request, response, this);
      }
   }
}

在这里,VirtualFilterChain会调用每个过滤的doFilter()方法进行处理。

内部结构

类图

FilterChainProxy

FilterChainProxy实现了Filter接口,说明FilterChainProxy也是一个过滤器。但是FilterChainProxy不执行过滤任务,而是调用getFilters()方法,筛选出与当前请求匹配的多个过滤器,然后将这些过滤器直接交给VirtualFilterChain进行处理。

FilterChain

这是一个过滤器链接口。

VirtualFilterChain

这是一个过滤器链实现类。该类实例化时会从FilterChainProxy接收多个Filter对象。当外部调用了该VirtualFilterChain对象的doFilter()时,则该VirtualFilterChain对象会确保所有Filter对象的doFilter()方法都被调用。

这里使用了职责链模式来实现这个功能。

小结

FilterChainProxy是一个过滤器代理,自身不执行过滤逻辑,但会筛选出符合条件的过滤器,并将这些过滤器交给VirtualFilterChain过滤器链对象。

VirtualFilterChain过滤器链对象不是过滤器,其通过职责链模式实现了将该链里的所有Filter按顺序执行下去。

相关推荐
ljh123321ljh6 分钟前
常见框架漏洞—Spring
java·数据库·spring
loveking68 分钟前
Springboot读取nacos配置
java·spring boot·后端
Asthenia041223 分钟前
面试复盘:JVM 与 Linux 中的线程进程上下文切换及子进程异常感知
后端
binnnngo26 分钟前
Spring Boot 实战:MD5 密码加密应用全解析
java·spring boot·后端
Asthenia041231 分钟前
用 Java 谈谈递归与回溯的差异性
后端
毕小宝1 小时前
JDBC批量查询语句 setFetchSize不生效问题分析
后端
信徒_1 小时前
Kubernetes 中导致 pod 重启的原因
java·容器·kubernetes
Asthenia04121 小时前
面试复盘:谈谈Linux在创建线程时做了什么工作/线程切换的成本如何估量?
后端
Aphelios3801 小时前
Java全栈面试宝典:内存模型与Spring设计模式深度解析
java·学习·spring·设计模式·云原生·面试
luoluoal1 小时前
java项目之基于ssm的车辆出租管理系统(源码+文档)
java·mysql·mybatis·ssm·源码