一、过滤器Filter基本知识

过滤器Filter就是可以实现web容器对某资源的访问前截获进行相关的处理,还可以在某资源向web容器返回响应前进行截获进行处理。
简单来说,过滤器就相当于是一个滤纸用来过滤条件的~~

public interface Filter {

   //这是Servlet过滤器的初始化方法,Servlet容器创建Servlet过滤器实例后将调用这个方法。
   //在这个方法中可以读取web.xml 文件中Servlet过滤器的初始化参数
    public default void init(FilterConfig filterConfig) throws ServletException {}

   //这个方法完成实际的过滤操作。当客户请求访问与过滤器关联的URL时,Servlet容器将先调用过滤器的doFilter方法。
   //FilterChain参数用于访问后续过滤器
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException;

    //Servlet容器在销毁过滤器实例前调用该方法,在这个方法中可以释放Servlet过滤器占用的资源下面创建一个NoteFilter过滤器
    //它可以拒绝列在黑名单上的客户访问留言簿, 
    public default void destroy() {}
}

过滤器Filter可以处理请求也可以处理响应,但是他们执行顺序是相反的
请求时是:A --> B --> C
响应时是:C --> B --> A

在老项目中,Filter一般直接在web.xml文件中配置。利用配置过滤器名称,类以及过滤器在链中的位置。
而在Spring Boot项目中,则可以通过FilterRegisterBean来配置过滤器。

二、SpringSecurity过滤器执行过程浅析

SpringSecurity项目有2种过滤器链FilterChain,一个种tomcta的ApplicationFilterChain,另一个是Spring的VirtualFilterChain。

spring为啥要实现自己的FilterChain?

Spring Security Filter 并不是直接嵌入到 Web Filter 中的,而是通过 FilterChainProxy 来统一管理 Spring Security Filter,FilterChainProxy 本身则通过 Spring 提供的 DelegatingFilterProxy 代理过滤器嵌入到 Web Filter 之中。

FilterChainProxy有点像spring mvc的分发器dispatcherservlet,Spring Security自己的Filter,其实我们可以只认为只有一个,那就是DelegatingFilterProxy,因为Spring Security中只有这个Filter是直接嵌入到 Web Filter,而其他的Spring Security Filter是通过DelegatingFilterProxy代理的FilterChainProxy来管理的。有点像dispatcherservlet管里各个接口。其概念图如下

网关过滤器与spring security 过滤器哪个先执行 springsecurity过滤器顺序_初始化


ApplicationFilterChain管理的Filter如下

网关过滤器与spring security 过滤器哪个先执行 springsecurity过滤器顺序_spring boot_02


DelegatingFilterProxy在第四个,其doFilter方法是SpringSecurity的入口

三、DelegatingFilterProxy

Spring web在设计的时候考虑到某些功能的实现是通过Filter来拦截进行实现的,如果直接的简单的实现几个Filter好像也不是不可以(平时我们就是这么用的),但是Spring框架最核心的是IOC容器,和Spring框架最好的实现就是将要实现的Filter功能注册到IOC容器的一个Bean,这样就可以和Spring IOC容器进行完美的融合,所以Spring Web设计了DelegatingFilterProxy。

3.1 代理Filter的初始化

本质上来说DelegatingFilterProxy就是一个Filter,其间接实现了Filter接口,但是在doFilter中其实调用的从Spring 容器中获取到的代理Filter的实现类delegate。

这段代码发生init方法中,在请求的第一次调用时执行,延迟了代理Filter的初始化

public class DelegatingFilterProxy extends GenericFilterBean {

    @Nullable
	private String targetBeanName;//代理Filter的名字是springSecurityFilterChain

    @Override
	protected void initFilterBean() throws ServletException {
		synchronized (this.delegateMonitor) {
			if (this.delegate == null) {
				if (this.targetBeanName == null) {
					this.targetBeanName = getFilterName();
				}
				WebApplicationContext wac = findWebApplicationContext();
				if (wac != null) {
					this.delegate = initDelegate(wac);
				}
			}
		}
	}
 protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
		String targetBeanName = getTargetBeanName();
		Assert.state(targetBeanName != null, "No target bean name set");
		//纯SpringSecurity项目delegate是FilterChainProxy
		Filter delegate = wac.getBean(targetBeanName, Filter.class);
		if (isTargetFilterLifecycle()) {
			delegate.init(getFilterConfig());
		}
		return delegate;
	}
}
3.2 代理Filter的调用

发生在doFilter方法中,请求每次调用都会调用这个方法

public class DelegatingFilterProxy extends GenericFilterBean {

    @Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
        //第一次执行这个方法时,delegateToUse一般为null
        //delegateToUse在调用时在加载,属于懒加载
		Filter delegateToUse = this.delegate;
		if (delegateToUse == null) {
			synchronized (this.delegateMonitor) {
				delegateToUse = this.delegate;
				if (delegateToUse == null) {
					WebApplicationContext wac = findWebApplicationContext();
					if (wac == null) {
						throw new IllegalStateException("");
					}
					//delegateToUse加载
					//delegateToUse是FilterChainProxy
					delegateToUse = initDelegate(wac);
				}
				this.delegate = delegateToUse;
			}
		}
		//底层逻辑时执行代理Filter的的doFilter方法
		//村SpringSecurity代理Filter是FilterChainProxy
		invokeDelegate(delegateToUse, request, response, filterChain);
	}
}
三、FilterChainProxy
public class FilterChainProxy extends GenericFilterBean {

	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);
		}
	}
}

这段代码逻辑,我们可以不用关注,因为他不管怎么走,都会执行其doFilterInternal方法

public class FilterChainProxy extends GenericFilterBean {

 private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

		FirewalledRequest fwRequest = firewall.getFirewalledRequest((HttpServletRequest) request);
		HttpServletResponse fwResponse = firewall.getFirewalledResponse((HttpServletResponse) response);

        //SpringSecurity一般都会注册很多默认Filter,
        //注意这些Filter在容器启动时就初始化好了
		List<Filter> filters = getFilters(fwRequest);

		if (filters == null || filters.size() == 0) {
			if (logger.isDebugEnabled()) {
				logger.debug();
			}
			fwRequest.reset();
			chain.doFilter(fwRequest, fwResponse);
			return;
		}
        //VirtualFilterChain是一个过滤器链FilterChain
		VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
		vfc.doFilter(fwRequest, fwResponse);
	}
}

VirtualFilterChain管理的Filter如下

网关过滤器与spring security 过滤器哪个先执行 springsecurity过滤器顺序_Web_03


1、WebAsyncManagerIntegrationFilter :Web异步管理集成过滤器, 使得异步线程可以从SecurityContextHolder中获取上下文信息

2、SecurityContextPersistenceFilter:两个主要职责:请求来临时,创建SecurityContext安全上下文信息,请求结束时清空SecurityContextHolder。

3、HeaderWriterFilter:(文档中并未介绍,非核心过滤器) 用来给http响应添加一些Header,比如X-Frame-Options, X-XSS-Protection*,X-Content-Type-Options.

4、CsrfFilter:在spring4这个版本中被默认开启的一个过滤器,用于防止csrf攻击,了解前后端分离的人一定不会对这个攻击方式感到陌生,前后端使用json交互需要注意的一个问题。

5、LogoutFilter:顾名思义,处理注销的过滤器

6、 UsernamePasswordAuthenticationFilter:这个会重点分析,表单提交了username和password,被封装成token进行一系列的认证,便是主要通过这个过滤器完成的,在表单认证的方法中,这是最最关键的过滤器。

7、 DefaultLoginPageGeneratingFilter:默认登录页面 生成器 过滤器

8、DefaultLogoutPageGeneratingFilter:默认退出登录页面 生成器 过滤器

9、BasicAuthenticationFilter:Basic认证方式过滤器

10、RequestCacheAwareFilter:(文档中并未介绍,非核心过滤器) 内部维护了一个RequestCache,用于缓存request请求

11、SecurityContextHolderAwareRequestFilter:此过滤器对ServletRequest进行了一次包装,使得request具有更加丰富的API

12、AnonymousAuthenticationFilter:匿名身份过滤器,这个过滤器个人认为很重要,需要将它与UsernamePasswordAuthenticationFilter 放在一起比较理解,spring security为了兼容未登录的访问,也走了一套认证流程,只不过是一个匿名的身份。

13、SessionManagementFilter : 和session相关的过滤器,内部维护了一个SessionAuthenticationStrategy,两者组合使用,常用来防止session-fixation protection attack,以及限制同一用户开启多个会话的数量

14、ExceptionTranslationFilter:直译成异常翻译过滤器,还是比较形象的,这个过滤器本身不处理异常,而是将认证过程中出现的异常交给内部维护的一些类去处理,具体是那些类下面详细介绍

15、FilterSecurityInterceptor :这个过滤器决定了访问特定路径应该具备的权限,访问的用户的角色,权限是什么?访问的路径需要什么样的角色和权限?这些判断和处理都是由该类进行的

后面逻辑就是进入各个过滤器执行他们的doFilter方法