zoukankan      html  css  js  c++  java
  • Spring Security addFilter() 顺序问题(转)

    Spring Security addFilter() 顺序问题

    一、 分析

    在上面Spring Security + Jwt 项目的搭建过程中,我一直有一个问题。我们假设我们使用addFilterAt(A, B.class)。 即将A拦截器添加到B拦截器的位置。那么addFilterAt 既然没有覆盖原先的拦截器,那么A不是在B拦截器前面就是在B拦截器后面,那么岂不是和addFilterBefore 或者 addFilterAfter 重复了?

    然而元芳告诉我没那么简单。最下面有总结,不想看源码的小伙伴可以直接看总结。

    通过断点看到。我们通过添加拦截器的代码是这个样子的。
    在这里插入图片描述
    那么我们看源码:

    0. FilterComparator

    为了更好的理解后面说的内容,我们需要先了解一下 FilterComparator 这个类。顾名思义,这个类是一个过滤器比较器。可以看到如下
    在这里插入图片描述

    其比较的规则如下, getOrder是根据类名来获取到过滤器的序号。可以看到过滤器的排序是根据排序序号的大小来排序的,序号小的在前,大的灾后。

    	public int compare(Filter lhs, Filter rhs) {
    		Integer left = getOrder(lhs.getClass());
    		Integer right = getOrder(rhs.getClass());
    		return left - right;
    	}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    我们这里拿 addFilterAt 举例,addFilterBefore 和 addFilterAfter 类似

    1. HttpSecurity#addFilterAt

    在这里插入图片描述
    一句一句看,先看第一句

    1.1 this.comparator.registerAt(filter.getClass(), atFilter);

    这里的 方法是 在 FilterComparator#registerAt 中
    这里可以看到,当我们使用addFilterAt 添加过滤器时,他添加的排序序号是和我们指定的拦截器相同的。()

    	
    	public void registerAt(Class<? extends Filter> filter,
    			Class<? extends Filter> atFilter) {
    		// 获取 atFilter 的顺序
    		Integer position = getOrder(atFilter);
    		if (position == null) {
    			throw new IllegalArgumentException(
    					"Cannot register after unregistered Filter " + atFilter);
    		}
    		// 将filter放入 filterToOrder 中, filterToOrder 是一个map集合
    		put(filter, position);
    	}
    	....
    	private void put(Class<? extends Filter> filter, int position) {
    		String className = filter.getName();
    		filterToOrder.put(className, position);
    	}
    	// 调用  addFilterBefore 时使用这个方法注册,这里可以看到排序序号比指定过滤器要小1
    	public void registerBefore(Class<? extends Filter> filter,
    			Class<? extends Filter> beforeFilter) {
    		Integer position = getOrder(beforeFilter);
    		if (position == null) {
    			throw new IllegalArgumentException(
    					"Cannot register after unregistered Filter " + beforeFilter);
    		}
    
    		put(filter, position - 1);
    	}
    	// 调用  addFilterAfter 时使用这个方法注册,这里可以看到排序序号比指定过滤器要大1
    	public void registerAfter(Class<? extends Filter> filter,
    			Class<? extends Filter> afterFilter) {
    		Integer position = getOrder(afterFilter);
    		if (position == null) {
    			throw new IllegalArgumentException(
    					"Cannot register after unregistered Filter " + afterFilter);
    		}
    
    		put(filter, position + 1);
    	}
    
    • 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

    简单来说,就是我们 添加的 jwtLoginFilter 拦截器和 UsernamePasswordAuthenticationFilter 的序号是相同的。

    1.2 HttpSecurity#addFilter(Filter filter)

    代码很简单,如下,将添加的过滤器加到 filters 集合中

    	public HttpSecurity addFilter(Filter filter) {
    		Class<? extends Filter> filterClass = filter.getClass();
    		if (!comparator.isRegistered(filterClass)) {
    			throw new IllegalArgumentException(
    					"The Filter class "
    							+ filterClass.getName()
    							+ " does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead.");
    		}
    		this.filters.add(filter);
    		return this;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    通过断点我们还可以发现,我们手动添加的过滤器是先于大部分系统过滤器被加入到filters集合中的。这一点可以从断点中看到
    在这里插入图片描述

    另外从 HttpSecurity#performBuild 方法中我们可以知道,filters 在添加完所有过滤器后,使用 FilterComparator 比较器进行了比较。结果呼之欲出。

    二、总结:

    1. FilterComparator 比较器中初始化了Spring Security 自带的Filter 的顺序,即在创建时已经确定了默认Filter的顺序。并将所有过滤器保存在一个 filterToOrder Map中。key值是Filter的类名,value是过滤器的顺序号。
    2. 当我们调用 HttpSecurity#addFilterAt(A, B.class) 方法时(其中B一定是先于A添加,或者B本身就是默认的过滤器),他会将我们的添加的过滤器A在 FilterComparator ,并给给我们一个和B相同的序号(addFilterBefore(A, B.class) 给A的序号比B小1,addFilterAfter(A, B.class) 给A的序号比B大1)。同时,HttpSecurity#addFilter(Filter filter) 会将我们添加的过滤器添加在 filters List集合中, 而在List集合汇总我们手动添加的拦截器在除了 WebAsyncManagerIntegrationFilter 之外的所有系统默认的拦截器之前。
    3. 最后Spring Security 会调用HttpSecurity#performBuild 方法,在这里会使用 FilterComparator 比较器对 filters进行比较排序,序号小的在前,序号大的在后,序号相等则按照原先的filters中的顺序。
    4. 由于在 filters List集合中,我们自己添加的过滤器要在除了 WebAsyncManagerIntegrationFilter 之外的所有系统默认的拦截器之前。导致了当我们调用了 HttpSecurity#addFilterAt(A, B.class) 方法时,A拦截器要先于B拦截器执行。

    举例(为了好理解,纯属假设):

    1. 假设Spring Security 里面默认拦截器 有A、B、C 三个(在强调一遍,假设有这三个),那么 FilterComparator 构造函数中就有这三个拦截器的顺序值,假设Map值为{“AcName” : 100, “BcName” : 200, “CcName” : 300}。其中AcName是A拦截器的类名,BC同理,100,200,300是他们的序号,用于确定顺序。
    2. 我们添加一个拦截器 F 要调用addFilterAt(F, B.class)。那么它首先会在 FilterComparator 中注册F (保存在排序Map中),排序序号和B相同,也为 200,这时候Map 就变成了{“AcName” : 100, “BcName” : 200, “CcName” : 300, “FcName” : 200}。(如果调用addFilterBefore F的序号就会减1,变成199; 如果调用addFilterAfter F的序号就会加1,变成201)
    3. 在 完成上述步骤后,HttpSecurity 中也会把 F拦截器添加到一个待排序的List集合L中,然后在添加其他系统默认过滤器(相当于这个List保存了所有的过滤器,但是其调用顺序未确定,还需要经过排序后才能确定)。最终List集合L就变成了{ A, F, B, C} (之前写成了F,A,B,C了,经评论区指正,已修改),注意这个是未经排序的过滤器集合,排序后才是真正的调用顺序。
      4.在调用 HttpSecurity#performBuild 方法时,会将HttpSecurity 中的过滤器集合L进行排序,排序比较器就是FilterComparator ,排序规则就是谁的排序序号小谁在前,序号大的在后,序号相同的保持 L 中的顺序。然后,得出最后的过滤器顺序,也就是最终调用顺序。

    (讲的略乱,我已经尽力了。。。)

    以上:内容部分木有参考
    如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正

  • 相关阅读:
    2019 icpc南昌全国邀请赛-网络选拔赛J题 树链剖分+离线询问
    Android小项目之十二 设置中心的界面
    【Mood-5】14条建议,使你的IT职业生涯更上一层楼
    【Android 界面效果15】Android UI 之一步步教你自定义控件(自定义属性、合理设计onMeasure、合理设计onDraw等)
    单线程模型中Message、Handler、Message Queue、Looper之间的关系
    140个google面试题
    Android小项目之十一 应用程序的主界面
    Android小项目之十 应用程序更新的签名问题
    Android小项目之九 两种上下文的区别
    Android小项目之八 界面细节
  • 原文地址:https://www.cnblogs.com/yasepix/p/14185097.html
Copyright © 2011-2022 走看看