Spring Security Web : Web安全过滤器链代理对象 FilterChainProxy

FilterChainProxySpring Security Web添加到Servlet容器用于安全控制的一个Filter。从Servlet容器的角度来看,Spring Security Web所提供的安全逻辑就是一个Filter,实现类为FilterChainProxy。实际上FilterChainProxy是一个代理对象,FilterChainProxy内部组合了多个SecurityFilterChain,每个SecurityFilterChain组合了一组Filter,这组Filter虽然也实现了Servlet Filter接口,但它们对于整个Servlet容器来讲是不可见的。对于Servlet容器来讲,Spring Secrutiy Web添加进来的用于安全的过滤器就是FilterChainProxy这一个过滤器,真正对请求的安全处理逻辑,最终由匹配该请求的某个SecurityFilterChain中的多个Filter来完成。

Spring Security Web框架中,FilterChainProxyWeb安全构建器WebSecurity构建而来。而该构建动作在应用启动过程中Web安全配置阶段执行,具体可以参考Web安全配置类WebSecurityConfiguration。而WebSecurityConfiguration则又由注解@EnableWebSecurity引起。这个触发关系,可以这么理解 :

@EnableWebSecurity => WebSecurityConfiguration => WebSecurity 构建 => FilterChainProxy

缺省情况下FilterChainProxy安全过滤器的名字总是springSecurityFilterChain

源代码

源代码版本 : Spring Security Web 5.1.4.RELEASE

package org.springframework.security.web;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.firewall.FirewalledRequest;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.firewall.StrictHttpFirewall;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

public class FilterChainProxy extends GenericFilterBean {
	// ~ Static fields/initializers
	// ======================================================

	private static final Log logger = LogFactory.getLog(FilterChainProxy.class);

	// ~ Instance fields
	// ======================================================

	private final static String FILTER_APPLIED = FilterChainProxy.class.getName().concat(
			".APPLIED");

	private List<SecurityFilterChain> filterChains;

	private FilterChainValidator filterChainValidator = new NullFilterChainValidator();

	private HttpFirewall firewall = new StrictHttpFirewall();

	// ~ Methods
	// =====================================================

	public FilterChainProxy() {
	}

    // 构造函数 :基于一组过滤器链 SecurityFilterChain 构造一个 FilterChainProxy 对象
	public FilterChainProxy(SecurityFilterChain chain) {
		this(Arrays.asList(chain));
	}

    // 构造函数 :基于一组过滤器链 SecurityFilterChain 构造一个 FilterChainProxy 对象
	public FilterChainProxy(List<SecurityFilterChain> filterChains) {
		this.filterChains = filterChains;
	}

	//  InitializingBean 接口定义的当前 bean 的初始化方法
	//  使用 filterChainValidator 验证当前对象
	@Override
	public void afterPropertiesSet() {
		filterChainValidator.validate(this);
	}

    // Filter  接口定义的过滤器主方法 :
    // 如果针对该请求尚未应用该过滤器则应用该过滤器
	@Override
	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 {
			// 2019-05-19 : 此分支存在的目的尚未明确 
			doFilterInternal(request, response, chain);
		}
	}

    // 应用当前过滤器到请求的具体逻辑实现
	private void doFilterInternal(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {

		FirewalledRequest fwRequest = firewall
				.getFirewalledRequest((HttpServletRequest) request);
		HttpServletResponse fwResponse = firewall
				.getFirewalledResponse((HttpServletResponse) response);

		// 当前过滤器其实一个组合了多个过滤器链 SecurityFilterChain 的代理对象,
		//  现在找到匹配当前请求的那个 SecurityFilterChain 中的所有安全过滤器
		List<Filter> filters = getFilters(fwRequest);

		if (filters == null || filters.size() == 0) {
			if (logger.isDebugEnabled()) {
				logger.debug(UrlUtils.buildRequestUrl(fwRequest)
						+ (filters == null ? " has no matching filters"
								: " has an empty filter list"));
			}

			fwRequest.reset();

			// 如果针对当前请求没有匹配的安全过滤器,则继续执行过滤器链  chain
			chain.doFilter(fwRequest, fwResponse);

			return;
		}

		// 如果针对当前请求有相应的安全过滤器,则将这些安全过滤器组成一个 VirtualFilterChain,
		// 虚拟过滤器链,将各个安全过滤器应用到该 请求上,这些安全过滤器应用完之后,在继续
		// 应用 chain 上的其他过滤器到该请求
		VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
		vfc.doFilter(fwRequest, fwResponse);
	}

	/**
	 * Returns the first filter chain matching the supplied URL.
	 * 返回匹配指定请求的过滤器链中的过滤器,如果没有匹配的过滤器链则返回null
	 * @param request the request to match
	 * @return an ordered array of Filters defining the filter chain
	 */
	private List<Filter> getFilters(HttpServletRequest request) {
		for (SecurityFilterChain chain : filterChains) {
			if (chain.matches(request)) {
				return chain.getFilters();
			}
		}

		return null;
	}

	/**
	 * Convenience method, mainly for testing.
	 *
	 * @param url the URL
	 * @return matching filter list
	 */
	public List<Filter> getFilters(String url) {
		return getFilters(firewall.getFirewalledRequest((new FilterInvocation(url, "GET")
				.getRequest())));
	}

	/**
	 * @return the list of SecurityFilterChains which will be matched against and
	 * applied to incoming requests.
	 */
	public List<SecurityFilterChain> getFilterChains() {
		return Collections.unmodifiableList(filterChains);
	}

	/**
	 * Used (internally) to specify a validation strategy for the filters in each
	 * configured chain.
	 *
	 * @param filterChainValidator the validator instance which will be invoked on during
	 * initialization to check the FilterChainProxy instance.
	 */
	public void setFilterChainValidator(FilterChainValidator filterChainValidator) {
		this.filterChainValidator = filterChainValidator;
	}

	/**
	 * Sets the "firewall" implementation which will be used to validate and wrap (or
	 * potentially reject) the incoming requests. The default implementation should be
	 * satisfactory for most requirements.
	 *
	 * @param firewall
	 */
	public void setFirewall(HttpFirewall firewall) {
		this.firewall = firewall;
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("FilterChainProxy[");
		sb.append("Filter Chains: ");
		sb.append(filterChains);
		sb.append("]");

		return sb.toString();
	}

	// ~ Inner Classes 内部类
	// =====================================================

	/**
	 * Internal FilterChain implementation that is used to pass a request through
	 * the additional internal list of filters which match the request.
	 * 一个内部类实现,用于将一组内部管理的过滤器应用到指定请求 firewalledRequest 上,应用完
	 * 这组过滤器之后,继续执行传入的过滤器链 chain
	 */
	private static class VirtualFilterChain implements FilterChain {
		private final FilterChain originalChain;
		private final List<Filter> additionalFilters;
		private final FirewalledRequest firewalledRequest;
		private final int size;
		private int currentPosition = 0;

		private VirtualFilterChain(FirewalledRequest firewalledRequest,
				FilterChain chain, List<Filter> additionalFilters) {
			this.originalChain = chain;
			this.additionalFilters = additionalFilters;
			this.size = additionalFilters.size();
			this.firewalledRequest = firewalledRequest;
		}

		@Override
		public void doFilter(ServletRequest request, ServletResponse response)
				throws IOException, ServletException {
			if (currentPosition == size) {
				if (logger.isDebugEnabled()) {
					logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
						+ " reached end of additional filter chain; proceeding with original chain");
				}

				// Deactivate path stripping as we exit the security filter chain
				this.firewalledRequest.reset();

				originalChain.doFilter(request, response);
			}
			else {
				currentPosition++;

				Filter nextFilter = additionalFilters.get(currentPosition - 1);

				if (logger.isDebugEnabled()) {
					logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
							+ " at position " + currentPosition + " of " + size
							+ " in additional filter chain; firing Filter: '"
							+ nextFilter.getClass().getSimpleName() + "'");
				}

				nextFilter.doFilter(request, response, this);
			}
		}
	}

	public interface FilterChainValidator {
		void validate(FilterChainProxy filterChainProxy);
	}

	private static class NullFilterChainValidator implements FilterChainValidator {
		@Override
		public void validate(FilterChainProxy filterChainProxy) {
		}
	}

}

相关文章

Spring Security Config : WebSecurityConfiguration Web 安全配置
Spring Boot 自动配置 : SecurityAutoConfiguration
Spring Security Web : SecurityFilterChain 安全过滤器概念模型

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值