首页 > 编程 > Java > 正文

详解spring security filter的工作原理

2019-11-26 08:46:44
字体:
来源:转载
供稿:网友

这篇文章介绍filter的工作原理。配置方式为xml。

Filter如何进入执行逻辑的

初始配置:

 <filter>  <filter-name>springSecurityFilterChain</filter-name>  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping>  <filter-name>springSecurityFilterChain</filter-name>  <url-pattern>/*</url-pattern> </filter-mapping>

DelegatingFilterProxy这个类继承了GenericFilterBean,GenericFilterBean实现了Filter接口。

这个配置是一切的开始,配置完这个之后,在启动项目的时候会执行Filterd的初始化方法:

@Override  public final void init(FilterConfig filterConfig) throws ServletException {    Assert.notNull(filterConfig, "FilterConfig must not be null");    if (logger.isDebugEnabled()) {      logger.debug("Initializing filter '" + filterConfig.getFilterName() + "'");    }    this.filterConfig = filterConfig;    // Set bean properties from init parameters.    PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);    if (!pvs.isEmpty()) {      try {        BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);        ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());        Environment env = this.environment;        if (env == null) {          env = new StandardServletEnvironment();        }        bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, env));        initBeanWrapper(bw);        bw.setPropertyValues(pvs, true);      }      catch (BeansException ex) {        String msg = "Failed to set bean properties on filter '" +          filterConfig.getFilterName() + "': " + ex.getMessage();        logger.error(msg, ex);        throw new NestedServletException(msg, ex);      }    }    // Let subclasses do whatever initialization they like.    initFilterBean(); // 这个方法    if (logger.isDebugEnabled()) {      logger.debug("Filter '" + filterConfig.getFilterName() + "' configured successfully");    }  }

在初始化方法中,会执行初始化Filter的方法initFilterBean。这个方法的实现在DelegatingFilterProxy中:

protected void initFilterBean() throws ServletException {    synchronized (this.delegateMonitor) {      if (this.delegate == null) {        // If no target bean name specified, use filter name.        if (this.targetBeanName == null) {          this.targetBeanName = getFilterName();        }        // Fetch Spring root application context and initialize the delegate early,        // if possible. If the root application context will be started after this        // filter proxy, we'll have to resort to lazy initialization.        WebApplicationContext wac = findWebApplicationContext();        if (wac != null) {          this.delegate = initDelegate(wac); //这个方法        }      }    }  }

在这个初始化方法中又调用initDelegate方法进行初始化:

protected Filter initDelegate(WebApplicationContext wac) throws ServletException {    String targetBeanName = getTargetBeanName();    Assert.state(targetBeanName != null, "No target bean name set");    Filter delegate = wac.getBean(targetBeanName, Filter.class);    if (isTargetFilterLifecycle()) {      delegate.init(getFilterConfig());    }    return delegate;  }

在这个方法中,先获取targetBeanName,这个名字是构造方法中赋值的:

public DelegatingFilterProxy(String targetBeanName, @Nullable WebApplicationContext wac) {    Assert.hasText(targetBeanName, "Target Filter bean name must not be null or empty");    this.setTargetBeanName(targetBeanName);    this.webApplicationContext = wac;    if (wac != null) {      this.setEnvironment(wac.getEnvironment());    }  }

这个名字就是web.xml中配置的名字springSecurityFilterChain:

 <filter>  <filter-name>springSecurityFilterChain</filter-name>  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter>

springSecurityFilterChain是固定不能改的,如果改了启动时就会报错,这是spring 启动时内置的一个bean,这个bean实际是FilterChainProxy。

这样一个Filter就初始化话好了,过滤器chain也初始化好了。

当一个请求进来的时候,会进入FilterChainProxy执行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);    }  }

先获取所有的Filter,然后执行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) {      if (logger.isDebugEnabled()) {        logger.debug(UrlUtils.buildRequestUrl(fwRequest)            + (filters == null ? " has no matching filters"                : " has an empty filter list"));      }      fwRequest.reset();      chain.doFilter(fwRequest, fwResponse);      return;    }    // 最终执行下面的这些代码    VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);    vfc.doFilter(fwRequest, fwResponse);  }

VirtualFilterChain是一个匿名内部类:

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;    }    @Override    public void doFilter(ServletRequest request, ServletResponse response)        throws IOException, ServletException {      if (currentPosition == size) {        if (logger.isDebugEnabled()) {          logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)              + " reached end of additional filter chain; proceeding with original chain");        }        // Deactivate path stripping as we exit the security filter chain        this.firewalledRequest.reset();        originalChain.doFilter(request, response);      }      else {        currentPosition++;        Filter nextFilter = additionalFilters.get(currentPosition - 1);        if (logger.isDebugEnabled()) {          logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)              + " at position " + currentPosition + " of " + size              + " in additional filter chain; firing Filter: '"              + nextFilter.getClass().getSimpleName() + "'");        }        nextFilter.doFilter(request, response, this);      }    }  }

filter集合执行的逻辑在VirtualFilterChain的doFilter方法中。

filter是如何执行的

上面说了怎么才能进入filter的执行逻辑,下面说一下filter到底怎么执行,为什么一个

在VirtualFilterChain的doFilter方法可以执行所有的filter。

下面写一个例子,模拟filter的执行逻辑。
定义FilterChain接口、Filter接口:

public interface Filter {  void doFilter(String username, int age, FilterChain filterChain);}
public interface FilterChain {  void doFilter(String username, int age);}

定义两个Filter实现:

public class NameFilter implements Filter {  @Override  public void doFilter(String username, int age, FilterChain filterChain) {    username = username + 1;    System.out.println("username: " + username + "  age: " + age);    System.out.println("正在执行:NameFilter");    filterChain.doFilter(username, age);  }}
public class AgeFilter implements Filter {  @Override  public void doFilter(String username, int age, FilterChain filterChain) {    age += 10;    System.out.println("username: " + username + "  age: " + age);    System.out.println("正在执行:AgeFilter");    filterChain.doFilter(username, age);  }}

定义一个FilterChain实现:

public class FilterChainProxy implements FilterChain {  private int position = 0;  private int size = 0;  private List<Filter> filterList = new ArrayList<>();  public void addFilter(Filter filter) {    filterList.add(filter);    size++;  }  @Override  public void doFilter(String username, int age) {    if (size == position) {      System.out.println("过滤器链执行结束");    } else {      Filter filter = filterList.get(position);      position++;      filter.doFilter(username, age, this);    }  }}

测试Filter实现:

public class FilterTest {  public static void main(String[] args) {    FilterChainProxy proxy = new FilterChainProxy();    proxy.addFilter(new NameFilter());    proxy.addFilter(new AgeFilter());    proxy.doFilter("张三", 0);  }}=======username: 张三1  age: 0正在执行:NameFilterusername: 张三1  age: 10正在执行:AgeFilter过滤器链执行结束

在这个执行逻辑中,最重要的是【this】,this就是初始化的好的FilterChain实例,在这个测试实例中,this就是FilterChainProxy。

执行FilterChainProxy的doFilter方法的时候,传入了初始参数username和age,进入这个方法后,根据position取出相应的Filter,初次进入position是0,执行Filter的doFilter方法,注意,此时Filter的doFilter方法额外传入了一个this参数,这个参数就是初始化的好的FilterChain实例,在Filter中的doFilter的方法中最后又会执行FilterChain的doFilter方法,相当于第二次调用FilterChain实例的doFilter方法,此时posotion是1,然后再执行Filter的doFilter方法,直到所有的Filter执行完,整个执行过程结束。

VirtualFilterChain的doFilter方法的执行逻辑和这个测试实例中的执行逻辑基本一致。

这样就完成了整个过滤器链的执行。

总结

以前用Filter的时候就非常疑惑过滤器怎么执行的,直到今天才算解决了这个疑惑。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持武林网。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表