SpringSecurity过滤器链深度解析
1. 简介
Spring Security的核心机制是一个精心设计的过滤器链(Filter Chain),它负责处理Web应用的安全需求。每个过滤器都有特定的职责,按照预定义的顺序执行,共同构成了完整的安全防护体系。理解过滤器链的工作原理对于深入掌握Spring Security、进行性能优化和问题排查至关重要。
本文将深入解析Spring Security过滤器链的组成、调用顺序、调度原理,并通过源码分析和实际案例帮助开发者全面理解这一核心机制。
2. Spring Security过滤器链概述
2.1 过滤器链的基本概念
Spring Security过滤器链是一个由多个安全过滤器组成的链式结构,每个过滤器负责处理特定的安全任务。当HTTP请求到达时,它会依次通过过滤器链中的各个过滤器,每个过滤器都可以对请求进行处理、修改或拦截。
// Spring Security过滤器链的基本结构
public interface SecurityFilterChain {
boolean matches(HttpServletRequest request);
List<Filter> getFilters();
}
// 默认过滤器链实现
public class DefaultSecurityFilterChain implements SecurityFilterChain {
private final RequestMatcher requestMatcher;
private final List<Filter> filters;
@Override
public boolean matches(HttpServletRequest request) {
return requestMatcher.matches(request);
}
@Override
public List<Filter> getFilters() {
return this.filters;
}
}2.2 过滤器链的创建过程
Spring Security在启动时会自动创建和配置过滤器链:
// 过滤器链的创建过程
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz.anyRequest().authenticated())
.formLogin(withDefaults())
.httpBasic(withDefaults());
return http.build();
}
}
// HttpSecurity.build()方法会创建过滤器链
public final class HttpSecurity {
public SecurityFilterChain build() {
// 1. 创建过滤器列表
List<Filter> filters = new ArrayList<>();
// 2. 按顺序添加各个过滤器
filters.add(new WebAsyncManagerIntegrationFilter());
filters.add(new SecurityContextPersistenceFilter());
filters.add(new HeaderWriterFilter());
// ... 更多过滤器
// 3. 创建过滤器链
return new DefaultSecurityFilterChain(requestMatcher, filters);
}
}3. Spring Security核心过滤器详解
3.1 完整的过滤器调用顺序
Spring Security的默认过滤器链包含以下核心过滤器(按调用顺序排列):
// Spring Security默认过滤器顺序(从源码中提取)
public enum DefaultSecurityFilterChainOrder {
// 1. 异步管理器集成过滤器
WEB_ASYNC_MANAGER_FILTER(Ordered.HIGHEST_PRECEDENCE + 100),
// 2. 安全上下文持久化过滤器
SECURITY_CONTEXT_FILTER(Ordered.HIGHEST_PRECEDENCE + 200),
// 3. 头部写入过滤器
HEADER_WRITER_FILTER(Ordered.HIGHEST_PRECEDENCE + 300),
// 4. CSRF保护过滤器
CSRF_FILTER(Ordered.HIGHEST_PRECEDENCE + 400),
// 5. 注销过滤器
LOGOUT_FILTER(Ordered.HIGHEST_PRECEDENCE + 500),
// 6. 请求缓存过滤器
REQUEST_CACHE_FILTER(Ordered.HIGHEST_PRECEDENCE + 600),
// 7. 认证处理过滤器
AUTHENTICATION_PROCESSING_FILTER(Ordered.HIGHEST_PRECEDENCE + 700),
// 8. 安全上下文持有者AOP过滤器
SECURITY_CONTEXT_HOLDER_AWARE_REQUEST_FILTER(Ordered.HIGHEST_PRECEDENCE + 800),
// 9. 匿名认证过滤器
ANONYMOUS_AUTHENTICATION_FILTER(Ordered.HIGHEST_PRECEDENCE + 900),
// 10. 会话管理过滤器
SESSION_MANAGEMENT_FILTER(Ordered.HIGHEST_PRECEDENCE + 1000),
// 11. 异常转换过滤器
EXCEPTION_TRANSLATION_FILTER(Ordered.HIGHEST_PRECEDENCE + 1100),
// 12. 授权过滤器
AUTHORIZATION_FILTER(Ordered.HIGHEST_PRECEDENCE + 1200);
}3.2 核心过滤器功能详解
3.2.1 SecurityContextPersistenceFilter
功能:负责在请求开始时加载Security Context,在请求结束时保存Security Context。
// SecurityContextPersistenceFilter源码分析
public class SecurityContextPersistenceFilter extends GenericFilterBean {
private SecurityContextRepository repo;
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// 1. 从存储中加载Security Context
SecurityContext contextBeforeChainExecution = repo.loadContext(request);
try {
// 2. 设置到SecurityContextHolder
SecurityContextHolder.setContext(contextBeforeChainExecution);
// 3. 继续过滤器链
chain.doFilter(request, response);
} finally {
// 4. 清理Security Context
SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
SecurityContextHolder.clearContext();
// 5. 保存Security Context
repo.saveContext(contextAfterChainExecution, request, response);
}
}
}3.2.2 UsernamePasswordAuthenticationFilter
功能:处理基于表单的用户名密码认证。
// UsernamePasswordAuthenticationFilter源码分析
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response)
throws AuthenticationException {
// 1. 检查请求方法
if (!"POST".equals(request.getMethod())) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
// 2. 提取用户名和密码
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
// 3. 创建认证令牌
UsernamePasswordAuthenticationToken authRequest =
new UsernamePasswordAuthenticationToken(username, password);
// 4. 设置详细信息
setDetails(request, authRequest);
// 5. 委托给AuthenticationManager进行认证
return this.getAuthenticationManager().authenticate(authRequest);
}
protected String obtainUsername(HttpServletRequest request) {
return request.getParameter(SPRING_SECURITY_FORM_USERNAME_KEY);
}
protected String obtainPassword(HttpServletRequest request) {
return request.getParameter(SPRING_SECURITY_FORM_PASSWORD_KEY);
}
}3.2.3 ExceptionTranslationFilter
功能:处理安全异常,将认证和授权异常转换为合适的HTTP响应。
// ExceptionTranslationFilter源码分析
public class ExceptionTranslationFilter extends GenericFilterBean {
private AuthenticationEntryPoint authenticationEntryPoint;
private AccessDeniedHandler accessDeniedHandler;
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
try {
// 1. 继续过滤器链
chain.doFilter(request, response);
} catch (Exception ex) {
// 2. 处理异常
Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);
// 3. 处理认证异常
RuntimeException ase = (AuthenticationException) this.throwableAnalyzer
.getFirstThrowableOfType(AuthenticationException.class, causeChain);
if (ase != null) {
handleAuthenticationException(request, response, chain, (AuthenticationException) ase);
return;
}
// 4. 处理授权异常
ase = (AccessDeniedException) this.throwableAnalyzer
.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
if (ase != null) {
handleAccessDeniedException(request, response, chain, (AccessDeniedException) ase);
return;
}
// 5. 重新抛出其他异常
rethrow(ex);
}
}
private void handleAuthenticationException(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain,
AuthenticationException failed) throws IOException, ServletException {
SecurityContextHolder.clearContext();
// 调用认证入口点
authenticationEntryPoint.commence(request, response, failed);
}
private void handleAccessDeniedException(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain,
AccessDeniedException failed) throws IOException, ServletException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || authentication instanceof AnonymousAuthenticationToken) {
// 匿名用户访问受保护资源
authenticationEntryPoint.commence(request, response,
new InsufficientAuthenticationException("Full authentication is required to access this resource"));
} else {
// 认证用户但权限不足
accessDeniedHandler.handle(request, response, failed);
}
}
}3.2.4 FilterSecurityInterceptor
功能:进行最终的授权决策,决定用户是否有权访问特定资源。
// FilterSecurityInterceptor源码分析
public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
private FilterInvocationSecurityMetadataSource securityMetadataSource;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
// 1. 调用前置处理
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
// 2. 继续过滤器链(这是最后一个过滤器)
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
// 3. 调用后置处理
super.finallyInvocation(token);
}
// 4. 调用后置处理
super.afterInvocation(token, null);
}
@Override
protected InterceptorStatusToken beforeInvocation(Object object) {
FilterInvocation fi = (FilterInvocation) object;
// 1. 获取配置属性(权限要求)
Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(fi);
if (attributes == null || attributes.isEmpty()) {
// 没有权限要求,直接放行
return null;
}
// 2. 检查认证状态
Authentication authenticated = authenticateIfRequired();
// 3. 进行授权决策
try {
this.accessDecisionManager.decide(authenticated, object, attributes);
} catch (AccessDeniedException accessDeniedException) {
// 授权失败
publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, accessDeniedException));
throw accessDeniedException;
}
// 4. 创建状态令牌
return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object);
}
}4. 过滤器链调度原理
4.1 过滤器链的执行流程
Spring Security过滤器链的执行遵循标准的Servlet过滤器模式,但增加了特定的安全处理逻辑:
// 过滤器链执行流程(简化版)
public class FilterChainProxy extends GenericFilterBean {
private List<SecurityFilterChain> filterChains;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 1. 转换为HTTP请求/响应
HttpServletRequest firewalledRequest = this.firewall.getFirewalledRequest((HttpServletRequest) request);
HttpServletResponse firewalledResponse = this.firewall.getFirewalledResponse((HttpServletResponse) response);
// 2. 查找匹配的过滤器链
List<Filter> filters = getFilters(firewalledRequest);
if (filters == null || filters.size() == 0) {
// 没有匹配的过滤器链,直接继续
chain.doFilter(firewalledRequest, firewalledResponse);
return;
}
// 3. 创建虚拟过滤器链
VirtualFilterChain virtualFilterChain = new VirtualFilterChain(firewalledRequest, chain, filters);
// 4. 执行过滤器链
virtualFilterChain.doFilter(firewalledRequest, firewalledResponse);
}
private List<Filter> getFilters(HttpServletRequest request) {
for (SecurityFilterChain chain : this.filterChains) {
if (chain.matches(request)) {
return chain.getFilters();
}
}
return null;
}
}
// 虚拟过滤器链实现
private static class VirtualFilterChain implements FilterChain {
private final FilterChain originalChain;
private final List<Filter> additionalFilters;
private final int size;
private int currentPosition = 0;
public VirtualFilterChain(HttpServletRequest request, 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 {
if (this.currentPosition == this.size) {
// 所有过滤器已执行完毕,继续原始链
this.originalChain.doFilter(request, response);
} else {
// 执行下一个过滤器
this.currentPosition++;
Filter nextFilter = this.additionalFilters.get(this.currentPosition - 1);
nextFilter.doFilter(request, response, this);
}
}
}4.2 过滤器链的匹配机制
Spring Security支持多个过滤器链,根据请求的URL路径进行匹配:
// 多过滤器链配置示例
@Configuration
@EnableWebSecurity
public class MultiChainSecurityConfig {
// 第一个过滤器链:管理端点
@Bean
@Order(1)
public SecurityFilterChain managementFilterChain(HttpSecurity http) throws Exception {
http
.securityMatcher("/actuator/**")
.authorizeHttpRequests(authz -> authz
.requestMatchers("/actuator/health").permitAll()
.requestMatchers("/actuator/**").hasRole("MONITOR")
)
.httpBasic(withDefaults());
return http.build();
}
// 第二个过滤器链:API端点
@Bean
@Order(2)
public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
http
.securityMatcher("/api/**")
.authorizeHttpRequests(authz -> authz
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/**").authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2.jwt(withDefaults()));
return http.build();
}
// 第三个过滤器链:默认链
@Bean
@Order(3)
public SecurityFilterChain defaultFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz.anyRequest().authenticated())
.formLogin(withDefaults());
return http.build();
}
}4.3 过滤器链的调试和监控
4.3.1 启用调试日志
# application.properties
logging.level.org.springframework.security=DEBUG
logging.level.org.springframework.security.web.FilterChainProxy=TRACE4.3.2 自定义调试过滤器
// 调试过滤器,用于监控过滤器链执行
@Component
public class DebugFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(DebugFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
long startTime = System.currentTimeMillis();
String requestId = UUID.randomUUID().toString();
HttpServletRequest httpRequest = (HttpServletRequest) request;
logger.info("请求开始: {} {} - {}",
httpRequest.getMethod(), httpRequest.getRequestURI(), requestId);
try {
// 继续过滤器链
chain.doFilter(request, response);
} finally {
long duration = System.currentTimeMillis() - startTime;
logger.info("请求完成: {} {} - {} - 耗时: {}ms",
httpRequest.getMethod(), httpRequest.getRequestURI(), requestId, duration);
}
}
}
// 注册调试过滤器
@Configuration
@EnableWebSecurity
public class DebugSecurityConfig {
@Autowired
private DebugFilter debugFilter;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.addFilterBefore(debugFilter, SecurityContextPersistenceFilter.class)
.authorizeHttpRequests(authz -> authz.anyRequest().authenticated());
return http.build();
}
}5. 自定义过滤器开发
5.1 创建自定义认证过滤器
// JWT认证过滤器
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenProvider tokenProvider;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
try {
// 1. 从请求中提取JWT令牌
String jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
// 2. 从JWT中提取用户名
String username = tokenProvider.getUsernameFromJWT(jwt);
// 3. 加载用户详情
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
// 4. 创建认证对象
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// 5. 设置到安全上下文
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception ex) {
logger.error("无法设置用户认证", ex);
}
// 6. 继续过滤器链
filterChain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
// 注册JWT过滤器
@Configuration
@EnableWebSecurity
public class JwtSecurityConfig {
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeHttpRequests(authz -> authz.anyRequest().authenticated());
return http.build();
}
}5.2 创建自定义授权过滤器
// IP白名单过滤器
@Component
public class IpWhitelistFilter extends OncePerRequestFilter {
private final List<String> allowedIps = Arrays.asList(
"192.168.1.100", "192.168.1.101", "10.0.0.0/8"
);
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String clientIp = getClientIpAddress(request);
if (!isIpAllowed(clientIp)) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.getWriter().write("Access denied from IP: " + clientIp);
return;
}
filterChain.doFilter(request, response);
}
private String getClientIpAddress(HttpServletRequest request) {
String xForwardedFor = request.getHeader("X-Forwarded-For");
if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
return xForwardedFor.split(",")[0].trim();
}
return request.getRemoteAddr();
}
private boolean isIpAllowed(String ip) {
for (String allowedIp : allowedIps) {
if (allowedIp.contains("/")) {
// CIDR表示法
if (isInRange(ip, allowedIp)) {
return true;
}
} else {
// 精确IP匹配
if (ip.equals(allowedIp)) {
return true;
}
}
}
return false;
}
private boolean isInRange(String ip, String cidr) {
// CIDR范围检查实现
// 简化实现,实际应使用专业库如IPAddress
return true;
}
}6. 过滤器链性能优化
6.1 过滤器执行顺序优化
// 优化过滤器顺序配置
@Configuration
@EnableWebSecurity
public class OptimizedSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// 1. 将轻量级过滤器放在前面
.addFilterBefore(new CustomHeaderFilter(), SecurityContextPersistenceFilter.class)
// 2. 认证相关过滤器
.addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
// 3. 授权相关配置
.authorizeHttpRequests(authz -> authz
.requestMatchers("/public/**").permitAll()
.requestMatchers("/api/**").authenticated()
.anyRequest().denyAll() // 明确拒绝其他请求,避免不必要的过滤器执行
)
// 4. 禁用不必要的安全特性
.csrf().disable() // 如果使用JWT等无状态认证
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
return http.build();
}
}6.2 缓存优化
// 用户详情缓存
@Component
public class CachingUserDetailsService implements UserDetailsService {
private final UserDetailsService delegate;
private final Cache userCache;
public CachingUserDetailsService(UserDetailsService delegate) {
this.delegate = delegate;
this.userCache = new ConcurrentMapCache("userDetails");
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 从缓存获取
Cache.ValueWrapper wrapper = userCache.get(username);
if (wrapper != null) {
return (UserDetails) wrapper.get();
}
// 缓存未命中,从委托服务加载
UserDetails userDetails = delegate.loadUserByUsername(username);
// 放入缓存(设置合适的过期时间)
userCache.put(username, userDetails);
return userDetails;
}
}7. 常见问题与解决方案
7.1 过滤器顺序问题
问题:自定义过滤器未按预期顺序执行。
解决方案:使用明确的顺序控制。
@Configuration
@EnableWebSecurity
public class OrderFixConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// 明确指定过滤器顺序
.addFilterBefore(customFilter1(), SecurityContextPersistenceFilter.class)
.addFilterAfter(customFilter2(), UsernamePasswordAuthenticationFilter.class)
.addFilterAt(customFilter3(), SecurityContextPersistenceFilter.class);
return http.build();
}
}7.2 过滤器链匹配问题
问题:请求未匹配到预期的过滤器链。
解决方案:检查securityMatcher配置。
@Bean
@Order(1)
public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
http
.securityMatcher("/api/**") // 明确匹配规则
.authorizeHttpRequests(authz -> authz.anyRequest().authenticated());
return http.build();
}8. 总结
Spring Security过滤器链是一个精心设计的链式安全处理机制,通过多个过滤器的协同工作,为Web应用提供了全面的安全保护。理解过滤器链的组成、调用顺序和调度原理,对于深入掌握Spring Security、进行性能优化和问题排查至关重要。
关键要点:
- 过滤器顺序至关重要:每个过滤器都有特定的位置和职责
- 理解核心过滤器功能:SecurityContextPersistenceFilter、UsernamePasswordAuthenticationFilter等
- 掌握过滤器链调度原理:VirtualFilterChain的执行机制
- 合理使用自定义过滤器:扩展安全功能,满足特定业务需求
- 性能优化考虑:过滤器顺序、缓存策略、不必要的安全特性禁用
通过深入理解Spring Security过滤器链,开发者可以构建出既安全又高效的应用系统。