Spring Security是基于过滤器链来处理请求的,在Spring Security的启动流程一文中,讲到Spring Security注册到servlet上下文中的过滤器是DelegatingFilterProxy,而该过滤器代理了FilterChainProxy。本文就来分析一下请求是怎么在这些过滤器中流转的。
DelegatingFilterProxy
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// Lazily initialize the delegate if necessary.
Filter delegateToUse = this.delegate;
if (delegateToUse == null) {
synchronized (this.delegateMonitor) {
delegateToUse = this.delegate;
if (delegateToUse == null) {
// 获取应用上下文
WebApplicationContext wac = findWebApplicationContext();
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: " +
"no ContextLoaderListener or DispatcherServlet registered?");
}
// 从应用上下文中找过滤器代理
delegateToUse = initDelegate(wac);
}
// 保存下来,下次就不用再找了
this.delegate = delegateToUse;
}
}
// Let the delegate perform the actual doFilter operation.
// 调用目标过滤器
invokeDelegate(delegateToUse, request, response, filterChain);
}
protected void invokeDelegate(
Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
delegate.doFilter(request, response, filterChain);
}
这里首先是获取代理目标,一般来说在初始化阶段就会获取了,这里运行时判断是否是null,是的话会再次获取目标过滤器。最后调用目标过滤器的doFilter方法。
FilterChainProxy
/*
* 该类型的实例在WebSecurity的performBuild()方法中被创建,
* 被WebSecurityConfiguration中的springSecurityFilterChain()方法注入到容器中(该filter bean的名称为springSecurityFilterChain),
* 该方法在DelegatingFilterProxy中的invokeDelegate()方法中被调用
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
if (!clearContext) { // 已经设置过了FILTER_APPLIED属性,为什么已经设置过了该属性的的请求还会被该过滤器处理?
// 执行过滤器
doFilterInternal(request, response, chain);
return;
}
try {
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
// 执行过滤器
doFilterInternal(request, response, chain);
}
catch (Exception ex) {
Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);
Throwable requestRejectedException = this.throwableAnalyzer
.getFirstThrowableOfType(RequestRejectedException.class, causeChain);
if (!(requestRejectedException instanceof RequestRejectedException)) {
throw ex;
}
// 拒绝处理
this.requestRejectedHandler.handle((HttpServletRequest) request, (HttpServletResponse) response,
(RequestRejectedException) requestRejectedException);
}
finally {
this.securityContextHolderStrategy.clearContext();
request.removeAttribute(FILTER_APPLIED);
}
}
该方法只是对异常进行了处理,具体的实现在doFilterInternal方法中。
private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
FirewalledRequest firewallRequest = this.firewall.getFirewalledRequest((HttpServletRequest) request);
HttpServletResponse firewallResponse = this.firewall.getFirewalledResponse((HttpServletResponse) response);
// 获取目标过滤器集合
List<Filter> filters = getFilters(firewallRequest);
if (filters == null || filters.size() == 0) {
if (logger.isTraceEnabled()) {
logger.trace(LogMessage.of(() -> "No security for " + requestLine(firewallRequest)));
}
firewallRequest.reset();
this.filterChainDecorator.decorate(chain).doFilter(firewallRequest, firewallResponse);
return;
}
if (logger.isDebugEnabled()) {
logger.debug(LogMessage.of(() -> "Securing " + requestLine(firewallRequest)));
}
// 封装了后续其他的servlet过滤器
FilterChain reset = (req, res) -> {
if (logger.isDebugEnabled()) {
logger.debug(LogMessage.of(() -> "Secured " + requestLine(firewallRequest)));
}
// Deactivate path stripping as we exit the security filter chain
firewallRequest.reset();
// 继续调用后续的servlet filter
chain.doFilter(req, res);
};
// filterChainDecorator是VirtualFilterChain类型的
this.filterChainDecorator.decorate(reset, filters).doFilter(firewallRequest, firewallResponse);
}
该方法中先是根据请求获取目标过滤器列表,然后是利用过滤器装饰器对过滤器列表进行装饰,形成一条过滤器链,最后调用这条过滤器链。
获取目标过滤器列表
private List<Filter> getFilters(HttpServletRequest request) {
int count = 0;
// 遍历过滤器链
for (SecurityFilterChain chain : this.filterChains) {
if (logger.isTraceEnabled()) {
logger.trace(LogMessage.format("Trying to match request against %s (%d/%d)", chain, ++count,
this.filterChains.size()));
}
// 如果该过滤器链支持处理该请求,那么则返回该过滤器链上的所有过滤器
if (chain.matches(request)) {
// 找到一个就直接返回
return chain.getFilters();
}
}
return null;
}
这里遍历了过滤器链列表,然后调用它的matches方法来筛选出能够处理该请求的过滤器。这里的过滤器链是在WebSecurity的performBuild方法中创建时,通过构造方法参数传入的。一般这里就只有一条过滤器链,就是我们自定义并注册到bean工厂中的那个bean对象。我们通过HttpSecurity来构建的过滤器链是DefaultSecurityFilterChain类型的。
@SuppressWarnings("unchecked")
@Override
protected DefaultSecurityFilterChain performBuild() {
// URL表达式授权配置器
ExpressionUrlAuthorizationConfigurer<?> expressionConfigurer = getConfigurer(
ExpressionUrlAuthorizationConfigurer.class);
// http授权配置器
AuthorizeHttpRequestsConfigurer<?> httpConfigurer = getConfigurer(AuthorizeHttpRequestsConfigurer.class);
// 这里使用了异或运算符(^)
boolean oneConfigurerPresent = expressionConfigurer == null ^ httpConfigurer == null;
// 如果都为null或都不为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);
List<Filter> sortedFilters = new ArrayList<>(this.filters.size());
for (Filter filter : this.filters) {
// 添加到列表中
sortedFilters.add(((OrderedFilter) filter).filter);
}
// 创建过滤器链
return new DefaultSecurityFilterChain(this.requestMatcher, sortedFilters);
}
调用的getFilter方法返回的过滤器列表就是上面方法中的变量sortedFilters,这里通过对实例属性filters来封装的。我们可以直接调用HttpSecurity添加过滤器器的方法来添加过滤器,也可以通过不同的配置器(configurer)对象来配置逻辑,在这些配置器初始化的时候也会创建相应的过滤器。
进行装饰
在FilterChainProxy中创建的装饰器对象是VirtualFilterChainDecorator类型的。
public static final class VirtualFilterChainDecorator implements FilterChainDecorator {
/**
* {@inheritDoc}
*/
@Override
public FilterChain decorate(FilterChain original) {
return original;
}
/**
* {@inheritDoc}
*/
@Override
public FilterChain decorate(FilterChain original, List<Filter> filters) {
return new VirtualFilterChain(original, filters);
}
}
这里将多个过滤器封装为VirtualFilterChain。
执行过滤器链
private static final class VirtualFilterChain implements FilterChain {
// 其他的servlet上下文中的过滤器
private final FilterChain originalChain;
// Spring Security中的过滤器列表
private final List<Filter> additionalFilters;
private final int size;
private int currentPosition = 0;
private VirtualFilterChain(FilterChain chain, List<Filter> additionalFilters) {
this.originalChain = chain;
this.additionalFilters = additionalFilters;
this.size = additionalFilters.size();
}
@Override
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
// 如果已经执行完security相关的过滤器
if (this.currentPosition == this.size) {
// 则让其他servlet的filter被执行
this.originalChain.doFilter(request, response);
return;
}
this.currentPosition++;
// 获取下一个待执行的过滤器
Filter nextFilter = this.additionalFilters.get(this.currentPosition - 1);
if (logger.isTraceEnabled()) {
String name = nextFilter.getClass().getSimpleName();
logger.trace(LogMessage.format("Invoking %s (%d/%d)", name, this.currentPosition, this.size));
}
// 调用过滤器,并传入this作为过滤器链,过滤器内部可以决定要不要执行后面的其他过滤器。
nextFilter.doFilter(request, response, this);
}
}
在doFilter方法中,依次从列表中获取过滤器,并调用其doFilter方法,并传入this作为过滤器链。在执行完了Spring Security中的过滤器后,会执行其他servlet中的过滤器。上面方法中的nextFilter变量就是我们在业务中自定义的过滤器。