一、过滤器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管里各个接口。其概念图如下
ApplicationFilterChain管理的Filter如下
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如下
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方法