前言
经过前面十篇文章的流程分析,我们也了解了 SpringSecurity
的初始化流程和几种比较重要的过滤器。这里简单总结一下:
- 在 SpringSecurity - 启动流程分析(一) 中,我们知道
WebSecurityConfiguration
会在IoC 容器
中添加一个名称为springSecurityFilterChain
类型为Filter
的Bean
- 在 SpringSecurity - 启动流程分析(二) 中,我们知道这个
Filter
其实是FilterChainProxy
,并且是由HttpSecurity
生成的SecurityFilterChain
,其中包含了排好序的Filter
s - 在 SpringSecurity - 启动流程分析(三) 中,我们知道
FilterChainProxy
的执行是由它的内部类VirtualFilterChain
按照排好序的Filter
s 顺序执行的 - 在 SpringSecurity - 启动流程分析(四)- 默认过滤器 中,我们知道了
SpringSecurity
提供的12个默认过滤器
在这篇文章中,我们就来看一下 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));
}
而自定义的 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);
}
而那些默认的过滤器,其实也是通过 addFilter()
方法添加的,所以我们接下来主要看一下 HttpSecurity
的 addFilter()
方法
// 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
关于 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;
}
}
}
OrderedFilter
是 HttpSecurity
的内部类,实现了 Ordered
、Filter
接口:
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 + '}';
}
}
所有的 Filter
s 存储在 HttpSecurity
的 filters
属性中,最后由 performBuild()
方法封装为 DefaultSecurityFilterChain
:
注意:这里我使用的是
spring-security-5.7.2
跟spring-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);
}
总结
以上就是 SpringSecurity
中 Filter
的顺序,经过前面的分析,逻辑还是很清晰的,不得不说,Spring
的代码,真是一环套一环,跟标准答案一样。