SpringSecurity
基本功能
SpringSecurity提供三个基本的功能
- Authentication(身份认证)
- Authenrization(用户授权)
- 防御常见攻击
默认行为
- 保护应用程序URL,要求对应用程序的任何交互进行身份验证
- 程序启动时生成一个默认用户“user”
- 生成一个默认的随机密码,并将此密码记录在控制台上
- 生成默认的登录表单和注销页面
- 提供基于表单的登录和注销流程
- 对于web请求,重定向到登录页面
- 对于服务请求,返回401未经授权
- 处理跨站请求伪造(CSRF)攻击
- 处理会话劫持攻击
- 写入Strict-Transport-Security以确保HTTPS
- 写入X-Content-Type-0ptions以处理嗅探攻击
- 写入Cache Control头来保护经过身份验证的资源
- 写入X-Frame-0ptions以处理点击劫持攻击。
架构
Spring Security 对 Servlet 的支持是基于Servlet过滤器的,所以先看一下过滤器的一般作用是很有帮助的
我们可以为拦截器链(Filter Chain)注册多个Filter,标准的Filter能完成防止下游的 Filter 实例或 Servlet 被调用或修改下游的 Filter 实例和 Servlet 所使用的 HttpServletRequest 或 HttpServletResponse的工作
由于一个 Filter 只影响下游的 Filter 实例和 Servlet,所以每个 Filter 的调用顺序是非常重要的。
Spring Security 的核心思想是拦截器链(Filter Chain)。当一个请求进入 Spring 应用程序时,它会经过一系列的过滤器。Spring Security 将自己的安全逻辑作为这些过滤器的一部分插入到请求处理流程中。
Spring Security 提供了一个名为 DelegatingFilterProxy 的 Filter 实现,允许在 Servlet 容器的生命周期和 Spring 的 ApplicationContext 之间建立桥梁,将标准的 Servlet 过滤器链与 Spring 的应用上下文(ApplicationContext)连接起来。
为什么需要 DelegatingFilterProxy?
理解 DelegatingFilterProxy 的必要性,需要先了解 Servlet 容器和 Spring 容器之间的区别:
Servlet 容器 (Web 容器):
- 负责加载和管理标准的 Servlet 和 Filter。
- 它通过 web.xml 配置或 @WebFilter 注解来识别和实例化过滤器。
- Servlet 容器直接管理这些过滤器的生命周期(init()、doFilter()、destroy())。
- 缺点:Web 容器实例化和管理的过滤器是普通的 Java 对象,它们不在 Spring 容器的管理之下。这意味着这些过滤器不能直接注入 Spring Bean,不能利用 Spring 的依赖注入、事务管理等高级功能。
Spring 容器 (ApplicationContext):
- 负责管理 Spring Bean 的生命周期和依赖注入。
- Spring Security 的核心组件(如 FilterChainProxy,它包含了所有的安全过滤器)都是 Spring Bean。
因此衍生出另外一个问题:
Spring Security 的安全过滤器链 (FilterChainProxy) 是一个 Spring Bean,它需要被 Spring 容器管理才能利用依赖注入等功能。但是,Web 容器只认识标准的 Servlet Filter。如何让 Web 容器调用的过滤器,实际上是 Spring 容器中的一个 Bean 呢?
这便是 DelegatingFilterProxy 的用武之地。
DelegatingFilterProxy 的工作流程
- Web 容器加载 DelegatingFilterProxy:
你需要在 web.xml 中配置 DelegatingFilterProxy,或者使用 Spring Boot 自动配置。Web 容器会像加载任何其他 Servlet Filter 一样加载并初始化 DelegatingFilterProxy。
在 web.xml 中,通常会这样配置:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
这里的 filter-name (例如 springSecurityFilterChain) 是关键,它告诉 DelegatingFilterProxy 去 Spring 容器中寻找同名的 Bean。
-
DelegatingFilterProxy 查找 Spring Bean:
当 DelegatingFilterProxy 的 init() 方法被调用时,它不会像普通过滤器那样直接执行业务逻辑。
相反,它会利用 Spring 的 WebApplicationContextUtils 或其他机制,找到当前 Web 应用对应的 Spring ApplicationContext。
然后,它会根据其配置的 filter-name(例如 springSecurityFilterChain),从 Spring ApplicationContext 中查找对应的 Bean。
在 Spring Security 中,这个 Bean 通常就是 org.springframework.security.web.FilterChainProxy 的实例。FilterChainProxy 是 Spring Security 自己的核心过滤器,它内部管理着一系列的 Spring Security 过滤器。 -
请求委托:
当一个请求到达 DelegatingFilterProxy 的 doFilter() 方法时,DelegatingFilterProxy 会将这个请求委托给它在 Spring 容器中找到的那个 Bean(即 FilterChainProxy)。
也就是说,DelegatingFilterProxy 只是一个“占位符”或“入口”,实际的过滤逻辑是由 Spring 容器中的 FilterChainProxy 来执行的。 -
Spring Security 过滤器链执行:
一旦请求被委托给 FilterChainProxy,FilterChainProxy 就会按照其内部配置的顺序,依次调用 Spring Security 自己的过滤器(如 UsernamePasswordAuthenticationFilter、FilterSecurityInterceptor 等)。这些过滤器都是 Spring Bean,因此可以充分利用 Spring 的功能。
FilterChainProxy 的工作原理
FilterChainProxy 是 Spring Security 的核心组件之一,它是一个特殊的 Servlet Filter,但它本身并不直接处理安全逻辑。它的主要职责是管理和调度 Spring Security 内部的多个 SecurityFilterChain 实例,并根据当前的请求 URL 路径,将请求分派给匹配的 SecurityFilterChain 进行处理。
可以把 FilterChainProxy 理解为一个路由器或调度器,它位于整个 Spring Security 过滤器链的最顶层。
为什么需要FilterChainProxy?
在一个复杂的应用中,你可能希望对不同的 URL 模式应用不同的安全规则。例如:
- /api/** 路径可能需要基于 Token 的认证和特定的 API 权限。
- /admin/** 路径可能需要基于会话的表单认证和管理员角色。
- /public/** 路径可能完全不需要认证。
FilterChainProxy 允许你定义多个 SecurityFilterChain,每个链对应不同的 URL 模式和安全配置。
FilterChainProxy 的工作流程
- 被 DelegatingFilterProxy 调用
- 匹配 SecurityFilterChain:
- FilterChainProxy 内部维护着一个有序的 List。
- 当 FilterChainProxy 接收到请求后,它会遍历这个 SecurityFilterChain 列表。
- 对于列表中的每一个 SecurityFilterChain,它会检查该链的 RequestMatcher 是否与当前请求的 URL 路径匹配。FilterChainProxy 会选择第一个匹配成功的 SecurityFilterChain 来处理请求。一旦找到匹配的链,就会停止遍历。
- 执行匹配的 SecurityFilterChain
- 请求继续或终止:
- 如果 SecurityFilterChain 中的所有过滤器都成功执行,请求最终会到达目标 Servlet 或 Controller。
- 如果在任何一个安全过滤器中,认证失败、授权失败或发生了其他安全异常,请求可能会被终止,并根据配置重定向到登录页面、访问拒绝页面,或返回错误状态码。
SecurityFilterChain
在 Spring Security 中,SecurityFilterChain 是一个核心概念,它代表着一个特定的、有序的 Spring Security 过滤器集合,这些过滤器被应用到一组匹配的请求路径上。简单来说,SecurityFilterChain 定义了针对特定 URL 模式的一套安全规则和处理流程。
SecurityFilterChain 的组成
每个 SecurityFilterChain 主要由**RequestMatcher (请求匹配器)和List (过滤器列表)**两部分组成
SecurityFilterChain 的工作流程
- 一旦某个 SecurityFilterChain 被选中,FilterChainProxy 就会将请求和响应对象传递给该 SecurityFilterChain 内部的过滤器列表。
- 接着,这个列表中的所有过滤器会按它们定义的顺序依次执行。
- 每个过滤器在执行完自己的逻辑后,会调用 chain.doFilter(request, response) 将请求传递给链中的下一个过滤器。
- 如果某个过滤器判断请求不符合安全要求(例如,用户未认证或无权限),它可能会直接生成错误响应、重定向到登录页,或者抛出异常,从而中断过滤器链的执行,请求就不会再传递到后续的过滤器或目标 Controller。
配置 SecurityFilterChain
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/public/**").permitAll() // 允许访问 /public 下的所有资源
.requestMatchers("/admin/**").hasRole("ADMIN") // /admin 下的资源需要 ADMIN 角色
.anyRequest().authenticated() // 其他所有请求都需要认证
)
.formLogin(withDefaults()); // 启用表单登录
return http.build();
}
// 可以定义多个 SecurityFilterChain Bean,通过它们的顺序和 RequestMatcher 来区分
// @Bean
// @Order(1) // 确保这个链优先处理
// public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
// http
// .securityMatcher("/api/**") // 仅对 /api/** 路径生效
// .authorizeHttpRequests(authorize -> authorize
// .anyRequest().hasRole("API_USER") // /api/** 需要 API_USER 角色
// )
// .httpBasic(withDefaults()); // 为 /api/** 启用 HTTP Basic 认证
// return http.build();
// }
}
在上述示例中,filterChain 方法返回的 SecurityFilterChain 对象就是由 FilterChainProxy 管理的一个独立的安全过滤器链。HttpSecurity 提供了便捷的 API 来配置这个链内部的各种安全行为和过滤器。
总结
Spring Security 并没有将它内部的每一个安全过滤器都直接暴露给 Web 容器去管理。相反,它设计了一个中央调度器——FilterChainProxy。这个 FilterChainProxy 自己作为一个 Spring Bean,并由 DelegatingFilterProxy 这个“中介”间接地被 Web 容器调用。一旦请求到达 FilterChainProxy,它就会根据请求的 URL,智能地选择并执行由其内部管理的、为特定路径配置好的那一串(SecurityFilterChain)安全过滤器(它们也都是 Spring Bean)。这种设计带来了极大的灵活性、可维护性和强大的调试能力。