说明
我们都知道SpringSecurity是基于过滤器链执行的,过滤器链执行完了之后再调用业务方法,现在我们分析一下SpringSecurity的过滤器链是怎样的结构。
源码分析
为了显示多个链,我这里搭建的时候加入了spring-security-oauth2,我们默认的也差不多,只不过只有一个链。
由于SpringSecurity是基于过滤器的,我们首先得找到过滤器的结构,这样我们才能清晰整个流程
定位到security一个熟悉的过滤器,然后往前找吧,最好的就是从熟悉的登录过滤器UsernamePasswordAuthenticationFilter
打断点往回找,如下图

上图5号即SpringSecurity自带的封装如下:

从UsernamePasswordAuthenticationFilter
定位到FilterChainProxy.VirtualFilterChain
了,VirtualFilterChain
是经典的SpringSecurity封装之后的过滤器链,它由它的代理类FilterChainProxy
管理,它才是真正继承Filter的类。相当于SpringSecurity所有相关的过滤器都是在这里面实现,对于外部来说它只占用了一个过滤器,而它自己内部又启了一个(可以类比成外部的过滤器是数组,SpringSecurity自己实现的是一个链表,链表执行完了继续执行FilterChainProxy
之后数组位置的过滤器)。
从上面的图可以看出,FilterChainProxy
(也就是delegate
属性)持有多个filterChains,后面我们从源码分析,它会根据url从多个链中选择一个封装成VirtualFilterChain
并执行。
源码如下:
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
| public class FilterChainProxy extends GenericFilterBean {
private List<SecurityFilterChain> filterChains; public FilterChainProxy(List<SecurityFilterChain> filterChains) { this.filterChains = filterChains; } @Override 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); } } else { doFilterInternal(request, response, chain); } }
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) { chain.doFilter(fwRequest, fwResponse); return; } VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters); vfc.doFilter(fwRequest, fwResponse); } private List<Filter> getFilters(HttpServletRequest request) { for (SecurityFilterChain chain : filterChains) { if (chain.matches(request)) { return chain.getFilters(); } } return null; } }
|
多个过滤器链filterChains(如果是单纯的SpringSecurity项目,则filterChains中只有一个值)

从上面已经知道SpringSecurity把自己封装成链了,并且把原始链带了进来(因为不带进来原始链后面的就没法执行了)。
我们再来分析VirtualFilterChain
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
| public class FilterChainProxy extends GenericFilterBean {
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) { originalChain.doFilter(request, response); } else { currentPosition++; Filter nextFilter = additionalFilters.get(currentPosition - 1); nextFilter.doFilter(request, response, this); } } } }
|
VirtualFilterChain
的执行也是比较纯粹的,就是把自己的执行完了之后再执行原始链。
我们执行登录的时候选择的是上图12号的过滤器链执行,即工程中配置的
1 2 3 4 5 6 7 8 9
| @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("放行的资源路径,也就是不鉴权").permitAll() .anyRequest().authenticated() .and() .formlogin() ;
|
12号过滤器链内部的过滤器则为:

至此,SpringSecuity过滤器链的分析就完成了。可以自己结合debug看会更清晰。