一 简介
我们只要学过Spring Sercurity,就知道这个框架其实是靠多个过滤器来实现。过滤器是servlet规范中的一种组件。我刚学的时候,也会想SpringSecurity就是把多个过滤器直接放入了过滤器链吗?还是说做了其他处理?
二 过滤器
首先我们先复习一下servlet中的过滤器,看下在常见的Spring框架下是如何运行的。
2.1 搭建环境并测试
创建一个springboot项目,目前只需要引入web依赖。
创建3个简单的过滤器
另外两个就是复制该类的,只是将A分别换成了B和C。
public class AFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("--A--");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("a init");
}
}
创建请求接口
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/get")
public String get(){
System.out.println("get request");
return "get api";
}
@GetMapping("/test")
public String test(){
System.out.println("test request");
return "test api";
}
}
创建配置类
@Configuration
public class MywebConfig implements WebMvcConfigurer {
@SuppressWarnings({ "rawtypes", "unchecked" })
@Bean
public FilterRegistrationBean AFilterRegist() {
FilterRegistrationBean frBean = new FilterRegistrationBean();
frBean.setFilter(new AFilter());
frBean.addUrlPatterns("/user/test");
System.out.println("注册 A");
frBean.setOrder(1);
return frBean;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Bean
public FilterRegistrationBean BFilterRegist() {
FilterRegistrationBean frBean = new FilterRegistrationBean();
frBean.setFilter(new BFilter());
frBean.addUrlPatterns("/user/test");
System.out.println("注册 B");
frBean.setOrder(2);
return frBean;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Bean
public FilterRegistrationBean CFilterRegist() {
FilterRegistrationBean frBean = new FilterRegistrationBean();
frBean.setFilter(new CFilter());
frBean.addUrlPatterns("/user/get");
System.out.println("注册 C");
frBean.setOrder(3);
return frBean;
}
}
现象
test接口被A、B过滤器拦截
get接口被C过滤器拦截
2.2 过滤器如何拦截指定路径
先在CFilter中的doFilter方法中打一个断点。
我们看下此时的过滤器链
除了我们自己写的CFilter,还有一些是框架自带的。我们先研究下,为什么A、B过滤器没被加入其中?
找到初始化过滤器链的代码
2.3 过滤器链是如何一个一个执行的
仔细看下过滤器链的doFIlter方法
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if( Globals.IS_SECURITY_ENABLED ) {
//没走这个分支,代码省略了
...
} else {
internalDoFilter(request,response);
}
}
再看internalDoFilter方法
其中在获取filterConfig对象时,还增加了pos的值
2.4 问题
基本上到这,我们已经大致搞清了过滤器拦截的部分流程。现在还有一个问题:SpringSecurity的过滤器也是直接加在这个过滤器里面的吗?
三 springSecurityFilterChain
现在几乎都是直接使用SpringBoot来构建项目了。SpringBoot帮我们做了很多事情,降低了开发的复杂度。但同时也容易让我们忽略框架中各个组件。
如果是用传统Spring来整合SpringSecurity,我们需要先在web.xml
中配置一个过滤器
<!--SpringSecurity核心过滤器链-->
<!--springSecurityFilterChain名词不能修改-->
<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>
这时可能会很疑惑?不是好多过滤器吗,怎么就只配了一个呢?
3.1 环境搭建
只要在pom文件中引入SpringSecurity的依赖就好了
然后我们继续请求get接口。并查看此时过滤器中到底几个过滤器
3.2 探查DelegatingFilterProxy类的初始化方法
构造方法
先在该类的构造方法打上断点
看上去没什么特殊的
init方法
因为DelegatingFilterProxy是继承了GenericFilterBean抽象了。DelegatingFilterProxy没有重写父类的init方法。也没有特殊的地方。
3.3 DelegatingFilterProxy类的doFilter
在该方法的第一行打上断点
第一次访问时会初始化
之后就不会了
继续跟踪进入initDelegate
方法
先看下这个过滤器对象(我称为代理过滤器
).里面竟然也有一个过滤器链
,只是目前只有一个过滤器
invokeDelegate
protected void invokeDelegate(
Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
delegate.doFilter(request, response, filterChain);
}
这个代理过滤器到底干了什么?放到第四节说明
四 FilterChainProxy
首先这是一个Filter,然后看名字。可以叫做过滤器链代理(就是第三节的代理过滤器)
4.1 继续查看doFilter方法
4.2 查看doFilterInternal
没想到这个FilterChainProxy真的类似过滤器类,获取了多个过滤器
跟进getFilters方法
4.3 VirtualFilterChain
VirtualFilterChain是FilterChainProxy的静态内部类,实现了FilterChain
接口。这就是SpringSecurity自己的一个过滤器链
关键字段
查看VirtualFilterChain的doFilter方法
SpringSecurity认证通过
这里的认证通过不是说必须要认证
。而是指SpringSecurity的所有过滤器都没有抛出异常即可(因为过滤器会对url进行匹配,只有匹配成功才会进行对应的逻辑处理)
五 小结
上面大体上说明了SpringSecurity的过滤器是加在哪里的?如何工作的?
引用官网上的一张图就是
至于各个过滤器都干了什么。本文就不展开讨论了