这部分是介绍责任链设计模式,因为spring security是基于过滤器链模式的
这里有一篇文章,很好地介绍了责任链模式
https://www.cnblogs.com/java-my-life/archive/2012/05/28/2516865.html
下面了解tomcat中过滤器链全过程
1.java servlet规范中,定义了过滤器和过滤器链技术
2.从tomcat的过滤器链开始
tomcat 的过滤器链是ApplicationFilterChain
启动一个springboot web项目,然后从浏览器发送一个请求,在过滤器链开始执行的地方debug看看,如下:
tomcat的过滤器链实现了servlet规范的过滤器链接口
第一个疑问是ApplicationFilterChain将所有的Filter存放在哪里?
答案是保存在ApplicationFilterChain类中的一个ApplicationFilterConfig对象的数组中。
那ApplicationFilterConfig对象又是什么呢?
ApplicationFilterConfig是一个Filter容器
ApplicationFilterConfig实现了过滤器配置接口FilterConfig,以前在web.xml中配置过滤器时如下:
当一个web应用首次启动时ApplicationFilterConfig会自动实例化,它会从该web应用的web.xml文件中读取配置的Filter的信息,然后装进该容器。
刚刚看到在ApplicationFilterChain类中所创建的ApplicationFilterConfig数组长度为零,那它是在什么时候被重新赋值的呢?
是在调用ApplicationFilterChain类的addFilter()方法时,下面源码截图
变量n用来记录当前过滤器链里面拥有的过滤器数目,默认情况下n等于0,ApplicationFilterConfig对象数组的长度也等于0,所以当第一次调用addFilter()方法时,if (n == filters.length)的条件成立,ApplicationFilterConfig数组长度被改变。之后filters[n++] = filterConfig;将变量filterConfig放入ApplicationFilterConfig数组中并将当前过滤器链里面拥有的过滤器数目+1。
那ApplicationFilterChain的addFilter()方法又是在什么地方被调用的呢?
是在ApplicationFilterFactory类的createFilterChain()方法中
查看源码createFilterChain()方法
第一目的是创建ApplicationFilterChain对象以及一些参数设置。
第二目的是从上下文中获取所有Filter信息,之后使用for循环遍历并调用filterChain.addFilter(filterConfig);将filterConfig放入ApplicationFilterChain对象的ApplicationFilterConfig数组中。
那ApplicationFilterFactory类的createFilterChain()方法又是在什么地方被调用的呢?
是在StandardWrapperValue类的invoke()方法中被调用的。看下图:
那正常的流程应该是这样的:
在StandardWrapperValue类的invoke()方法中调用ApplicationFilterChai类的createFilterChain()方法———>在ApplicationFilterChai类的createFilterChain()方法中调用ApplicationFilterChain类的addFilter()方法———>在ApplicationFilterChain类的addFilter()方法中给ApplicationFilterConfig数组赋值。
根据上面的代码可以看出StandardWrapperValue类的invoke()方法在执行完createFilterChain()方法后,会继续执行ApplicationFilterChain类的doFilter()方法,然后在doFilter()方法中会调用internalDoFilter()方法。
以下是internalDoFilter()方法的部分代码
// Call the next filter if there is one
if (pos < n) {
//拿到下一个Filter,将指针向下移动一位
//pos它来标识当前ApplicationFilterChain(当前过滤器链)执行到哪个过滤器
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = null;
try {
//获取当前指向的Filter的实例
filter = filterConfig.getFilter();
support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT,
filter, request, response);
if (request.isAsyncSupported() && "false".equalsIgnoreCase(
filterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
Boolean.FALSE);
}
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res, this};
SecurityUtil.doAsPrivilege
("doFilter", filter, classType, args, principal);
} else {
//调用Filter的doFilter()方法
filter.doFilter(request, response, this);
}
这里的filter.doFilter(request, response, this);就是调用我们前面创建的TestFilter中的doFilter()方法。而TestFilter中的doFilter()方法会继续调用chain.doFilter(request, response);方法,而这个chain其实就是ApplicationFilterChain,所以调用过程又回到了上面调用dofilter和调用internalDoFilter方法,这样执行直到里面的过滤器全部执行。
上图黄色框部分是自定义的两个过滤器