FilterChainProxy:请求的安全入口



FilterChainProxy 是 Spring Security 的核心 Filter,它拦截所有 HTTP 请求,将它们路由到合适的 SecurityFilterChain。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { List<SecurityFilterChain> chains = getFilterChains(); for (SecurityFilterChain chain : chains) { if (chain.matches(request)) { chain.doFilter(request, response, chain); return; } } chain.doFilter(request, response); }
|
SecurityFilterChain 的构成
每个 SecurityFilterChain 由多个 Filter 组成:
1 2 3 4 5 6 7 8 9 10 11 12
| SecurityFilterChain ├── SecurityContextPersistenceFilter # 管理 SecurityContext ├── LogoutFilter # 处理登出 ├── UsernamePasswordAuthenticationFilter # 处理表单登录 ├── BasicAuthenticationFilter # 处理 Basic 认证 ├── RequestCacheAwareFilter # 请求缓存 ├── SecurityContextHolderAwareRequestFilter # 请求包装 ├── RememberMeAuthenticationFilter # Remember-Me 认证 ├── AnonymousAuthenticationFilter # 匿名认证 ├── SessionManagementFilter # 会话管理 ├── ExceptionTranslationFilter # 异常处理 └── FilterSecurityInterceptor # 最终授权检查
|
OncePerRequestFilter:每个请求只执行一次
OncePerRequestFilter 是 FilterChainProxy 中大多数 Filter 的基类。它确保每个 Filter 在每个请求中只执行一次,防止重复执行导致的性能问题和状态混乱。
SecurityContextPersistenceFilter:上下文管理
这个 Filter 在每个请求开始前从 SecurityRepository 加载 SecurityContext,请求结束后保存。
1 2 3 4 5 6 7 8
| SecurityContext ctx = securityContextRepository.loadContext(holder); SecurityContextHolder.setContext(ctx);
securityContextRepository.saveContext(ctx, holder, afterRequestInProgress); SecurityContextHolder.clearContext();
|
默认使用 HttpSessionSecurityContextRepository,将 SecurityContext 存储在 HTTP Session 中。
ExceptionTranslationFilter:异常处理
这个 Filter 捕获安全相关的异常,转换为合适的 HTTP 响应:
| 异常 |
处理方式 |
AuthenticationException |
重定向到登录页或返回 401 |
AccessDeniedException |
返回 403 或重定向到拒绝页面 |
1 2 3 4 5
| catch (AuthenticationException authException) { authenticationEntryPoint.commence(request, response, authException); } catch (AccessDeniedException accessDeniedException) { AccessDeniedHandler.handle(request, response, accessDeniedException); }
|