• SpringSecurity - 启动流程分析(十一)- 过滤器的执行顺序


    前言

    经过前面十篇文章的流程分析,我们也了解了 SpringSecurity 的初始化流程和几种比较重要的过滤器。这里简单总结一下:

    在这篇文章中,我们就来看一下 SpringSecurity 是如何设置这些 Filter 的执行顺序的

    分析

    SpringSecurity - 启动流程分析(八)- CsrfFilter 过滤器SpringSecurity - 启动流程分析(九)- CorsFilter 过滤器 中,我们知道默认的 Filter 是通过 HttpSecurity 中的特定方法添加的,比如:

    public CorsConfigurer<HttpSecurity> cors() throws Exception {
    	return getOrApply(new CorsConfigurer<>());
    }
    
    public CsrfConfigurer<HttpSecurity> csrf() throws Exception {
    	ApplicationContext context = getContext();
    	return getOrApply(new CsrfConfigurer<>(context));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    而自定义的 Filter 则可以通过 addFilter() 等方法添加:

    @Override
    public HttpSecurity addFilterAfter(Filter filter, Class<? extends Filter> afterFilter) {
    	return addFilterAtOffsetOf(filter, 1, afterFilter);
    }
    
    @Override
    public HttpSecurity addFilterBefore(Filter filter, Class<? extends Filter> beforeFilter) {
    	return addFilterAtOffsetOf(filter, -1, beforeFilter);
    }
    
    private HttpSecurity addFilterAtOffsetOf(Filter filter, int offset, Class<? extends Filter> registeredFilter) {
    	int order = this.filterOrders.getOrder(registeredFilter) + offset;
    	this.filters.add(new OrderedFilter(filter, order));
    	this.filterOrders.put(filter.getClass(), order);
    	return this;
    }
    
    @Override
    public HttpSecurity addFilter(Filter filter) {
    	Integer order = this.filterOrders.getOrder(filter.getClass());
    	if (order == null) {
    		throw new IllegalArgumentException("The Filter class " + filter.getClass().getName()
    				+ " does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead.");
    	}
    	this.filters.add(new OrderedFilter(filter, order));
    	return this;
    }
    
    public HttpSecurity addFilterAt(Filter filter, Class<? extends Filter> atFilter) {
    	return addFilterAtOffsetOf(filter, 0, atFilter);
    }
    
    • 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

    而那些默认的过滤器,其实也是通过 addFilter() 方法添加的,所以我们接下来主要看一下 HttpSecurityaddFilter() 方法

    // HttpSecurity 被创建的时候,会初始化 FilterOrderRegistration
    private FilterOrderRegistration filterOrders = new FilterOrderRegistration();
    // 存放了所有的 Filter
    private List<OrderedFilter> filters = new ArrayList<>();
    
    // 这个方法用于 HttpSecurity 添加默认的过滤器
    @Override
    public HttpSecurity addFilter(Filter filter) {
    	Integer order = this.filterOrders.getOrder(filter.getClass());
    	if (order == null) {
    		throw new IllegalArgumentException("The Filter class " + filter.getClass().getName()
    				+ " does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead.");
    	}
    	this.filters.add(new OrderedFilter(filter, order));
    	return this;
    }
    
    // 上面的其他几个方法用于添加自定义的过滤器,比如 addFilterAt、addFilterBefore
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    关于 FilterOrderRegistration,这个类其实就是为了记录 Filter 的类名和顺序:

    final class FilterOrderRegistration {
    	// 初始顺序
    	private static final int INITIAL_ORDER = 100;
    	// 顺序步长
    	private static final int ORDER_STEP = 100;
    	// 以类名为key存放过滤器的顺序
    	private final Map<String, Integer> filterToOrder = new HashMap<>();
    
    	// 构造器中初始胡默认的过滤器顺序
    	FilterOrderRegistration() {
    		Step order = new Step(INITIAL_ORDER, ORDER_STEP);
    		put(DisableEncodeUrlFilter.class, order.next());
    		put(ForceEagerSessionCreationFilter.class, order.next());
    		put(ChannelProcessingFilter.class, order.next());
    		order.next(); // gh-8105
    		put(WebAsyncManagerIntegrationFilter.class, order.next());
    		put(SecurityContextHolderFilter.class, order.next());
    		put(SecurityContextPersistenceFilter.class, order.next());
    		put(HeaderWriterFilter.class, order.next());
    		put(CorsFilter.class, order.next());
    		put(CsrfFilter.class, order.next());
    		put(LogoutFilter.class, order.next());
    		this.filterToOrder.put(
    				"org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter",
    				order.next());
    		this.filterToOrder.put(
    				"org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationRequestFilter",
    				order.next());
    		put(X509AuthenticationFilter.class, order.next());
    		put(AbstractPreAuthenticatedProcessingFilter.class, order.next());
    		this.filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter", order.next());
    		this.filterToOrder.put("org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter",
    				order.next());
    		this.filterToOrder.put(
    				"org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter",
    				order.next());
    		put(UsernamePasswordAuthenticationFilter.class, order.next());
    		order.next(); // gh-8105
    		this.filterToOrder.put("org.springframework.security.openid.OpenIDAuthenticationFilter", order.next());
    		put(DefaultLoginPageGeneratingFilter.class, order.next());
    		put(DefaultLogoutPageGeneratingFilter.class, order.next());
    		put(ConcurrentSessionFilter.class, order.next());
    		put(DigestAuthenticationFilter.class, order.next());
    		this.filterToOrder.put(
    				"org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter",
    				order.next());
    		put(BasicAuthenticationFilter.class, order.next());
    		put(RequestCacheAwareFilter.class, order.next());
    		put(SecurityContextHolderAwareRequestFilter.class, order.next());
    		put(JaasApiIntegrationFilter.class, order.next());
    		put(RememberMeAuthenticationFilter.class, order.next());
    		put(AnonymousAuthenticationFilter.class, order.next());
    		this.filterToOrder.put("org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter",
    				order.next());
    		put(SessionManagementFilter.class, order.next());
    		put(ExceptionTranslationFilter.class, order.next());
    		put(FilterSecurityInterceptor.class, order.next());
    		put(AuthorizationFilter.class, order.next());
    		put(SwitchUserFilter.class, order.next());
    	}
    
    	// 存放指定类的顺序,如果重复则直接返回
    	void put(Class<? extends Filter> filter, int position) {
    		String className = filter.getName();
    		if (this.filterToOrder.containsKey(className)) {
    			return;
    		}
    		this.filterToOrder.put(className, position);
    	}
    
    	// 获取指定类的顺序,如果为空,尝试获取父类的顺序
    	Integer getOrder(Class<?> clazz) {
    		while (clazz != null) {
    			Integer result = this.filterToOrder.get(clazz.getName());
    			if (result != null) {
    				return result;
    			}
    			clazz = clazz.getSuperclass();
    		}
    		return null;
    	}
    
    	// 给定初始步数和步长,获取下一个步数
    	private static class Step {
    
    		private int value;
    
    		private final int stepSize;
    
    		Step(int initialValue, int stepSize) {
    			this.value = initialValue;
    			this.stepSize = stepSize;
    		}
    
    		int next() {
    			int value = this.value;
    			this.value += this.stepSize;
    			return value;
    		}
    
    	}
    
    }
    
    • 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
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103

    OrderedFilterHttpSecurity 的内部类,实现了 OrderedFilter 接口:

    private static final class OrderedFilter implements Ordered, Filter {
    
    	private final Filter filter;
    
    	private final int order;
    
    	private OrderedFilter(Filter filter, int order) {
    		this.filter = filter;
    		this.order = order;
    	}
    
    	@Override
    	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
    			throws IOException, ServletException {
    		this.filter.doFilter(servletRequest, servletResponse, filterChain);
    	}
    
    	@Override
    	public int getOrder() {
    		return this.order;
    	}
    
    	@Override
    	public String toString() {
    		return "OrderedFilter{" + "filter=" + this.filter + ", order=" + this.order + '}';
    	}
    
    }
    
    • 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

    所有的 Filters 存储在 HttpSecurityfilters 属性中,最后由 performBuild() 方法封装为 DefaultSecurityFilterChain

    注意:这里我使用的是 spring-security-5.7.2spring-security-5.4.6 代码还是有些不同的,不过殊途同归,都是使用 Comparator 做比较,关于 Comparator 不理解的可以查看 Java - 浅析 Comparable 和 Comparator 这篇文章。

    @Override
    protected DefaultSecurityFilterChain performBuild() {
    	ExpressionUrlAuthorizationConfigurer<?> expressionConfigurer = getConfigurer(
    			ExpressionUrlAuthorizationConfigurer.class);
    	AuthorizeHttpRequestsConfigurer<?> httpConfigurer = getConfigurer(AuthorizeHttpRequestsConfigurer.class);
    	boolean oneConfigurerPresent = expressionConfigurer == null ^ httpConfigurer == null;
    	Assert.state((expressionConfigurer == null && httpConfigurer == null) || oneConfigurerPresent,
    			"authorizeHttpRequests cannot be used in conjunction with authorizeRequests. Please select just one.");
    	// 排序
    	this.filters.sort(OrderComparator.INSTANCE);
    	// 排好序的 Filter
    	List<Filter> sortedFilters = new ArrayList<>(this.filters.size());
    	for (Filter filter : this.filters) {
    		sortedFilters.add(((OrderedFilter) filter).filter);
    	}
    	return new DefaultSecurityFilterChain(this.requestMatcher, sortedFilters);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    总结

    以上就是 SpringSecurityFilter 的顺序,经过前面的分析,逻辑还是很清晰的,不得不说,Spring 的代码,真是一环套一环,跟标准答案一样。

  • 相关阅读:
    个微多账号聚合聊天管理如何实现?
    IntelliJ IDEA个人可一直使用方法参考
    Vue3 源码解读系列(十一)——插槽 slot
    rust字面量
    package.json属性
    Simulink| “双碳”背景下汽车减少碳排放建模与仿真
    教你ehco、date、sep命令如何使用
    RocketMQ源码(13)—Broker 消息重放服务ReputMessageService源码解析
    C++前期概念(重)
    LocalDateTime获取月份第一天和最后一天
  • 原文地址:https://blog.csdn.net/qiaohao0206/article/details/126489334