FilterChainProxy
是Spring 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
框架中,FilterChainProxy
由Web
安全构建器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 安全过滤器概念模型