书单推荐:成为Java顶级程序员架构师 ,这20来本(高薪)必看点击获取
这篇文章介绍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 正在执行:NameFilter username: 张三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的时候就非常疑惑过滤器怎么执行的,直到今天才算解决了这个疑惑。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持谷谷点程序。
转载请注明:谷谷点程序 » 详解spring security filter的工作原理