Spring Security Filter架构总结
Spring Security 是一个提供身份验证、授权和防御常见攻击的框架。它对命令式和响应式应用程序的安全提供了一流的支持,是保护基于 Spring 的应用程序的事实标准。
这里先主要讲Servlet应用程序的架构,参考官方文档架构 :: Spring Security --- Architecture :: Spring Security和AI。
Servlet应用程序
Servlet Filter(Servlet 规范)
这是 Java Web 最底层的拦截机制,由 Servlet 容器(Tomcat、Jetty等)直接管理,Spring 本身没有发明新的 Filter 接口。
- 来源:
javax.servlet.Filter(Jakarta EE 之后包名变为jakarta.servlet) - 作用:可以在请求到达 Servlet 之前,或响应返回客户端之前,对请求/响应进行包装、修改或拦截。
- 生命周期:由容器管理,通过
web.xml或@WebFilter声明。
下图展示了单个 HTTP 请求的处理程序的典型分层结构。

Spring 如何把普通 Filter 纳入自己管理?
DelegatingFilterProxy
Spring 提供了一个名为 DelegatingFilterProxy Filter 实现,它允许在 Servlet 容器的生命周期和 Spring 的 ApplicationContext 之间建立桥接。Servlet 容器允许使用其自身的标准注册 Filter 实例,但它并不识别 Spring 定义的 Bean。您可以通过标准的 Servlet 容器机制注册 DelegatingFilterProxy ,但将所有工作委托给实现了 Filter 接口的 Spring Bean。

DelegatingFilterProxy 从 ApplicationContext 中查找 Bean 过滤器
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
Filter delegate = getFilterBean(someBeanName);
delegate.doFilter(request, response);
}
FilterChainProxy
Spring Security 的 Servlet 支持包含在 FilterChainProxy 中。 FilterChainProxy 是 Spring Security 提供的一种特殊 Filter ,它允许通过 SecurityFilterChain 将请求委托给多个 Filter 实例。由于 FilterChainProxy 是一个 Bean,因此它通常会被包装在 DelegatingFilterProxy 中(即上述代码的delegate)。
SecurityFilterChain
SecurityFilterChain 由 FilterChainProxy 使用,以确定应该为当前请求调用哪些 Spring Security Filter 实例。
相关源码:
// 获取匹配的filter
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;
}
// VirtualFilterChain
private VirtualFilterChain(FilterChain chain, List<Filter> additionalFilters) {
this.originalChain = chain;
this.additionalFilters = additionalFilters;
this.size = additionalFilters.size();
}
// 先按照顺序执行匹配的filter,最后再调用原始的filter
@Override
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
if (this.currentPosition == this.size) {
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));
}
nextFilter.doFilter(request, response, this);
}
如何添加自定义的Filter
安全过滤器通过 SecurityFilterChain API 插入到 FilterChainProxy 中。 这些过滤器可用于多种不同用途,例如 Spring Security 提供漏洞利用防护 、 身份验证 、 授权等功能。过滤器会按照特定的顺序执行,以确保在正确的时间被调用。例如,执行身份验证的 Filter 应该在执行授权的 Filter 之前调用。通常情况下,无需了解 Spring Security Filter 的执行顺序。但是,在某些情况下,了解执行顺序是有益的。如果您想了解具体顺序,可以查看 FilterOrderRegistration 代码 。
这些安全过滤器通常使用 HttpSecurity 实例声明。这也是代码中常见到的配置方式。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(Customizer.withDefaults())
.httpBasic(Customizer.withDefaults())
.formLogin(Customizer.withDefaults())
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
);
return http.build();
}
}
上述配置将产生以下 Filter 排序:
| Filter | Added by |
|---|---|
| CsrfFilter | HttpSecurity#csrf |
| BasicAuthenticationFilter | HttpSecurity#httpBasic |
| UsernamePasswordAuthenticationFilter | HttpSecurity#formLogin |
| AuthorizationFilter | HttpSecurity#authorizeHttpRequests |
添加的位置
HttpSecurity 提供了三种添加过滤器的方法:
#addFilterBefore(Filter, Class<?>)会在另一个过滤器之前添加你的过滤器。#addFilterAfter(Filter, Class<?>)会在另一个过滤器之后添加你的过滤器。#addFilterAt(Filter, Class<?>)会将另一个过滤器替换为您的过滤器。
比如我想再用户名密码验证之前添加JWT的过滤器。
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
默认顺序还是参考这个类FilterOrderRegistration:
private static final int INITIAL_ORDER = 100;
private static final int ORDER_STEP = 100;
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.web.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.web.authentication.Saml2WebSsoAuthenticationFilter",
order.next());
put(UsernamePasswordAuthenticationFilter.class, order.next());
order.next(); // gh-8105
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.authentication.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());
}
当问你如何自定义Filter顺序时,要注意什么?
注意区分问的是Spring Security内部管理的过滤器还是外部的的Servlet Filter,虽然他们都是运行在Servlet Filter层,但是生命周期是由Spring Security 控制。
Spring Security内部管理的过滤器
同上,通过HttpSecurity来设置它的位置
外部Servlet Filter,由Servlet容器管理
按照Order值来确定的:
@Order注解 标注在 Filter 类上(需实现Filter且被 Spring 管理)FilterRegistrationBean.setOrder(int)显式设置- 实现
org.springframework.core.Ordered接口
如springSecurityFilterChain默认值为SecurityProperties.DEFAULT_FILTER_ORDER = -100。
如果你想在 Spring Security 之前插入一个自定义 Servlet Filter(比如记录最原始的请求),就把它的 order 设得比 -100 更小,比如 -101:
@Bean
public FilterRegistrationBean<MyFilter> myFilter(){
FilterRegistrationBean<MyFilter> reg = new FilterRegistrationBean<>(new MyFilter());
reg.setOrder(-101);
return reg;
}
处理安全异常SecurityException
ExceptionTranslationFilter 允许将 AccessDeniedException 和 AuthenticationException 转换为 HTTP 响应。
ExceptionTranslationFilter 作为安全过滤器之一插入到 FilterChainProxy 中。

其实就是在正常filter上加了一个异常捕获,处理SecurityException。
原文链接:https://blog.dengwei.site/article/spring-security-filter-architecture-summary
本文来自博客园,作者:衣来伸手饭来张口,转载请注明原文链接:https://www.cnblogs.com/autowin/p/20342835

浙公网安备 33010602011771号