这篇文章介绍filter的工作原理。配置方式为xml。
初始配置:
<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到底怎么执行,为什么一个
在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的时候就非常疑惑过滤器怎么执行的,直到今天才算解决了这个疑惑。