模仿SpringSecurity自定义过滤器链

前言

想根据过滤器 filter 来实现 sdk 的一些功能, 但是配置多个过滤器又给调用方带来了配置的复杂性。所以这里模仿 SpringSecurity 中的 DelegatingFilterProxyFilterChainProxy 来实现了一个自定义的 FilterChain。源码参照 spring-boot-starter-security 中的源码, 相关代码位于 spring-security-web 包中

实现

1. 首先我们随意定义2个过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.example.filterchain.filter;

import javax.servlet.*;
import java.io.IOException;

/**
* @author cuishiying
* @date 2021-01-22
*/
public class AnonymousFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("AnonymousFilter init...");
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("AnonymousFilter doFilter...");
chain.doFilter(request, response);
}

@Override
public void destroy() {
System.out.println("AnonymousFilter destroy...");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.example.filterchain.filter;

import javax.servlet.*;
import java.io.IOException;

/**
* @author cuishiying
* @date 2021-01-22
*/
public class FormAuthenticationFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("FormAuthenticationFilter init...");
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("FormAuthenticationFilter doFilter...");
chain.doFilter(request, response);
}

@Override
public void destroy() {
System.out.println("FormAuthenticationFilter destroy...");
}
}

2. 用枚举将过自定义滤器管理起来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import org.springframework.objenesis.instantiator.util.ClassUtils;

import javax.servlet.Filter;

/**
* @author cuishiying
* @date 2021-01-22
*/
public enum DefaultFilter {

anon(AnonymousFilter.class),
authc(FormAuthenticationFilter.class);

private final Class<? extends Filter> filterClass;

private DefaultFilter(Class<? extends Filter> filterClass) {
this.filterClass = filterClass;
}

public Filter newInstance() {
return (Filter) ClassUtils.newInstance(this.filterClass);
}
}

3. 代理过滤器

代理过滤器也是 servlet 标准过滤器, 需要能够注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package com.example.filterchain.filter;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
* @author cuishiying
* @date 2021-01-22
*/
@Slf4j
public class FilterChainProxy implements Filter {

private List<Filter> filters;

@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filters = new ArrayList<>();
for (DefaultFilter defaultFilter : DefaultFilter.values()) {
this.filters.add(defaultFilter.newInstance());
}
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
log.info("FilterChainProxy.doFilter.uri:{}", httpServletRequest.getRequestURI());
doFilterInternal(request, response, chain);
}

private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
List<Filter> filters = getFilters();

// 如果当前过滤器链没有匹配的过滤器,则执行下一条过滤器链。
if (filters == null || filters.size() == 0) {
chain.doFilter(request, response);
return;
}

// 将所有的过滤器合并成一个虚拟过滤器链。
VirtualFilterChain vfc = new VirtualFilterChain(request, chain, filters);
// 执行虚拟过滤器链。
vfc.doFilter(request, response);
}

public List<Filter> getFilters() {
return filters;
}

public void setFilters(List<Filter> filters) {
this.filters = filters;
}

private class VirtualFilterChain implements FilterChain{

// 原始的过滤器链
private final FilterChain originalChain;
// 自定义过滤器链
private final List<Filter> additionalFilters;
// 自定义过滤器链长度
private final int size;
// 当前执行到过滤器链的位置
private int currentPosition = 0;

public VirtualFilterChain(ServletRequest request, FilterChain chain, List<Filter> additionalFilters) {
this.originalChain = chain;
this.additionalFilters = additionalFilters;
this.size = additionalFilters.size();
}

@Override
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {

if (currentPosition == size) {
// 如果当前虚拟过滤器链上的所有过滤器都已经执行完毕,则执行原生过滤器链上的剩余逻辑。
originalChain.doFilter(request, response);
} else {
currentPosition++;
// 获得当前虚拟过滤器链上的下一个过滤器。
Filter nextFilter = additionalFilters.get(currentPosition - 1);
nextFilter.doFilter(request, response, this);
}
}
}
}

4. 注册过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.example.filterchain.config;

import com.example.filterchain.filter.FilterChainProxy;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;

/**
* @author cuishiying
* @date 2021-01-22
*/
@Configuration
public class FilterConfig {

@Bean
public FilterRegistrationBean filterChainProxy() {
FilterRegistrationBean<FilterChainProxy> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new FilterChainProxy());
filterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE + 100);
filterRegistrationBean.setName("filterChainProxy");
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.setEnabled(true);
return filterRegistrationBean;
}
}

执行结果

1
2
3
2021-03-28 14:55:40.405  INFO 11175 --- [nio-8080-exec-1] c.e.filterchain.filter.FilterChainProxy  : FilterChainProxy.doFilter.uri:/actuator/prometheus
AnonymousFilter doFilter...
FormAuthenticationFilter doFilter...

我们可以看到首先执行了我们的标准代理过滤器 FilterChainProxy, 然后执行自定义的过滤器链

最后

本文到此结束,感谢阅读。如果您觉得不错,请关注公众号【当我遇上你】,您的支持是我写作的最大动力。

参考