SpringBoot–Spring Security(二)
一、概述
源码基于SpringBoot 2.7.xx版本
官网:https://spring.io/projects/spring-security
1.1 简介
Spring Security 是基于 Spring 的用户认证(Authentication)和用户授权(Authorization)框架,提供了一套 Web
应用安全性的完整解决方案。其中核心技术使用了 Servlet 过滤器、IOC 和 AOP 等。
- 用户认证:验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。
- 用户授权:验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。
一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。
1.1 名词解释
- Authentication 认证
- Authorization 授权
- AuthenticationManager 认证管理器
- AuthenticationProvider 身份认证提供程序
- AuthorizationManager 授权管理器
二、使用详解
2.1 Spring Security过滤器链执行流程
Spring Security的认证和授权是通过过滤器和AOP(基于方法的权限管理)来实现的,Spring Security
中的过滤器有很多,一般正常的项目中都有十几个过滤器,这里介绍几个重点过滤器。
DelegatingFilterProxy --> FilterChainProxy --> securityFilterChain
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=images%2FSpringBoot–Spring%20Security(%E4%BA%8C&pos_id=img-KgjCNShG-1705150358301)之Spring Security调用流程.png)
SpringSecurity 内置过滤器及其顺序
参照–FilterOrderRegistration
序号 | 过滤器名称 | 作用 | 是否默认加载 |
---|---|---|---|
1 | DisableEncodeUrlFilter | 主要是做URL编码方面的事情,主要功能是防止会话ID泄露和HTTP响应封装 | 是 |
2 | ForceEagerSessionCreationFilter | 主要是做会话方面的事情,比如主动创建会话 | 否 |
3 | ChannelProcessingFilter | 处理基于HTTP和HTTPS的请求的通道安全性 | 否 |
4 | WebAsyncManagerIntegrationFilter | 于集成 Spring MVC 的异步请求处理机制 | 是 |
5 | SecurityContextHolderFilter | “主要作用是管理 SecurityContext 的生命周期,确保每个请求都有一个正确的安全上下文。 这个过滤器与 SecurityContextPersistenceFilter 类似,但有一个关键区别:SecurityContextHolderFilter 不会在请求结束时自动保存 SecurityContext。” |
否 |
6 | SecurityContextPersistenceFilter | 主要负责在 HTTP 请求的生命周期内管理 SecurityContext 的加载和存储 | 是 |
7 | HeaderWriterFilter | “主要负责向HTTP响应中添加安全相关的头部信息。 这些头部信息可以包括例如 Content-Security-Policy、X-Frame-Options、X-Content-Type-Options 等,这些头部有助于减少如点击劫持、内容嗅探等安全威胁。” |
是 |
8 | CorsFilter | 用于处理跨源资源共享(CORS)的过滤器。CORS 是一种机制,允许或限制Web应用访问不同源(域、协议或端口)的资源。在现代Web应用中,CORS 是常见的需求,特别是在前后端分离的架构中。 | 否 |
9 | CsrfFilter | 用于提供跨站请求伪造(CSRF)保护。CSRF是一种攻击方式,攻击者诱使用户在已经认证的Web应用中执行非预期的操作。 | 是 |
10 | LogoutFilter | 负责处理用户的注销(logout)过程。这个过滤器会拦截注销请求,并执行相应的注销逻辑,如清除认证信息、使会话失效等。 | 是 |
11 | OAuth2AuthorizationRequestRedirectFilter | 用于处理 OAuth 2.0 授权请求的重定向 | 否 |
12 | Saml2WebSsoAuthenticationRequestFilter | 用于处理 SAML 2.0 Web 单点登录(SSO)认证请求 | 否 |
13 | X509AuthenticationFilter | 用于实现基于X.509证书的认证。X.509证书是一种标准格式的数字证书,广泛用于安全通信中,特别是在使用SSL/TLS的HTTPS连接中。这种认证机制允许服务器根据客户端提供的数字证书自动验证用户身份,通常用于企业环境或需要高安全性的应用中。 | 否 |
14 | AbstractPreAuthenticatedProcessingFilter | 用于处理预认证(pre-authenticated)场景的抽象基类过滤器。预认证指的是在应用程序外部已经完成用户的身份验证,比如通过外部代理、单点登录系统(SSO)或其他方式,而应用程序需要接受这种已有的认证并基于此建立安全上下文。 | 否 |
15 | CasAuthenticationFilter | 用于集成 Central Authentication Service (CAS) 单点登录(SSO)机制的过滤器。CAS 是一个广泛使用的Web单点登录协议,允许在多个应用间共享认证状态,即用户在一个应用中登录后,可以无需重新认证即可访问其他集成了CAS的应用。 | 否 |
16 | OAuth2LoginAuthenticationFilter | 用于处理 OAuth 2.0 登录,主要做拦截OAuth 2.0登录响应、换取访问令牌、用户信息获取等操作。 | 否 |
17 | Saml2WebSsoAuthenticationFilter | 用于处理基于SAML 2.0的Web单点登录(SSO)认证的过滤器。这个过滤器是 Spring Security 的 SAML 2.0 支持的一部分,主要负责处理从SAML 2.0身份提供者(Identity Provider, IdP)的响应。 | 否 |
18 | UsernamePasswordAuthenticationFilter | 处理包含用户名和密码表单登录的HTTP POST请求,通常用于标准的用户名/密码登录过程。 | 是 |
19 | OpenIDAuthenticationFilter | 用于处理基于 OpenID 的认证流程。然而,随着 OpenID Connect 的出现和逐渐取代传统的 OpenID,以及 Spring Security 对 OAuth 2.0 和 OpenID Connect 的支持,OpenIDAuthenticationFilter 的相关功能被淘汰。 | 否 |
20 | DefaultLoginPageGeneratingFilter | 用于在没有提供自定义登录页面时自动生成默认的登录页面。这个过滤器主要在开发和调试阶段有用,因为它提供了一个简单、立即可用的登录表单,无需开发者额外编写任何HTML或服务器端代码。 | 是 |
21 | DefaultLogoutPageGeneratingFilter | 用于自动生成默认注销(logout)页面 | 是 |
22 | ConcurrentSessionFilter | 用于处理并发会话管理的过滤器。它主要用于限制用户的同时登录会话数量,并在达到会话限制时采取相应的行动。这种机制对于增强应用程序的安全性非常有用,尤其是在需要防止同一个账户被多人同时使用的场景中。 | 否 |
23 | DigestAuthenticationFilter | 用于处理 HTTP 摘要认证的过滤器。HTTP摘要认证是一种比基本认证(basic authentication)更安全的认证机制,它避免了密码以明文形式在网络中传输。 | 否 |
24 | BearerTokenAuthenticationFilter | 用于处理基于令牌(Token)的认证的过滤器,特别是在处理 OAuth 2.0 Bearer 令牌时。这种类型的过滤器通常用于 RESTful API 和其他无状态服务中,它允许通过HTTP请求的 Authorization 头部中的令牌来认证用户。 | 否 |
25 | BasicAuthenticationFilter | 用于处理基本认证,即通过HTTP消息头传递的用户名和密码。在进行基本认证时,前端应用程序会将用户凭据(base64编码的)添加到HTTP请求标头中。 | 是 |
26 | RequestCacheAwareFilter | 保留请求数据并在认证成功后重定向回原始请求。这对于在用户登录后重定向回他们原先尝试访问的URL非常有用。 | 是 |
27 | SecurityContextHolderAwareRequestFilter | 包装原始的HttpServletRequest并提供额外的安全相关的方法,如isUserInRole | 是 |
28 | JaasApiIntegrationFilter | 用于集成Java Authentication and Authorization Service (JAAS) API。它执行JAAS登录并将结果放入Spring Security的安全上下文中 | 否 |
29 | RememberMeAuthenticationFilter | 处理“记住我”认证,这通常涉及到检查Cookies并在会话中自动认证用户 | 否 |
30 | AnonymousAuthenticationFilter | 为没有登录的用户创建一个匿名的认证令牌,使系统可以识别未登录用户 | 是 |
31 | OAuth2AuthorizationCodeGrantFilter | 用于处理OAuth 2.0授权码授予流程,处理从授权服务器返回的授权码 | 否 |
32 | SessionManagementFilter | 管理会话,包括会话固定保护和并发会话控制 | 是 |
33 | ExceptionTranslationFilter | 捕获Spring Security过程中的异常,并将它们转换为相应的HTTP响应或重定向 | 是 |
34 | FilterSecurityInterceptor | 已被AuthorizationFilter取代,负责在请求执行之前检查访问权限,基于配置的安全规则 | 是 |
35 | AuthorizationFilter | 这个过滤器不是Spring Security的标准部分,可能是特定应用或框架的一部分,用于特定类型的授权决策 | 否 |
36 | SwitchUserFilter | 允许用户切换或扮演另一个用户,通常用于管理员调试或用户支持 | 否 |
关于 AuthorizationFilter 取代 FilterSecurityInterceptor 官方说明:
AuthorizationFilter
2.1.1 DisableEncodeUrlFilter
这个过来器是为了 禁用URL重新编码的。
2.1.2 WebAsyncManagerIntegrationFilter
负责在异步处理过程中同步安全上下文,以确保安全上下文能够正确传播到异步执行的线程中。这对于处理异步请求非常重要,因为在异步处理中,线程可能会发生切换,
而安全上下文的正确传递对于安全操作至关重要。
默认创建 HttpSecurity 时会设置该Filter,HttpSecurity.addFilter(new WebAsyncManagerIntegrationFilter())。
public final class WebAsyncManagerIntegrationFilter extends OncePerRequestFilter {
private static final Object CALLABLE_INTERCEPTOR_KEY = new Object();
@Override
private void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 尝试获取一个 WebAsyncManager
// asyncManagerAttr 如果已存在,返回已存在的对象,
// asyncManagerAttr 为空,并将新对象放到servletRequest.setAttribute(WEB_ASYNC_MANAGER_ATTRIBUTE, asyncManager);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
// 尝试从asyncManager 中获取已经存储的拦截器
SecurityContextCallableProcessingInterceptor securityProcessingInterceptor = (SecurityContextCallableProcessingInterceptor) asyncManager
.getCallableInterceptor(CALLABLE_INTERCEPTOR_KEY);
// 如果没有获取到
if (securityProcessingInterceptor == null) {
asyncManager.registerCallableInterceptor(CALLABLE_INTERCEPTOR_KEY,
// 新建一个拦截器
new SecurityContextCallableProcessingInterceptor());
}
// 继续后续拦截器执行
filterChain.doFilter(request, response);
}
}
默认情况下securityContextHolderStrategy的存储策略为ThreadLocal,在ThreadLocal的存储策略下,只有当前线程可以获取到securityContextHolder。
WebAsyncManagerIntegrationFilter 通过创建拦截器的形式,将securityContextHolderStrategy传递给子线程,后续子线程可以通过该拦截器获取到用户认证信息。
2.1.3 SecurityContextHolderFilter
持有SecurityContext的过滤器。
默认创建 HttpSecurity 时会没有设置该Filter。
public class SecurityContextHolderFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
SecurityContext securityContext = this.securityContextRepository.loadContext(request).get();
try {
SecurityContextHolder.setContext(securityContext);
filterChain.doFilter(request, response);
} finally {
SecurityContextHolder.clearContext();
}
}
}
2.1.4 SecurityContextPersistenceFilter
这个过滤器在 Spring Boot2.7.xx 中废弃了,但是还在使用,在 Spring Boot3 中则被从 Spring Security 过滤器链中移除了,取而代之的是一个名为
SecurityContextHolderFilter 的过滤器。
它有两个作用:
- 当请求到来时,检查Session中是否存在SecurityContext,如果不存在,就创建一个新的SecurityContext。
- 请求结束时将SecurityContext放入Session中,并清空 SecurityContextHolder。
默认创建 HttpSecurity 时会设置该Filter,HttpSecurity.securityContext(withDefaults())。
2.1.5 HeaderWriterFilter
请求头写入过滤器,,他的作用是将某些头信息添加到响应中,添加某些启用浏览器保护的头信息非常有用,如X-Frame-Options、X-XSS-Protection、
X-Content-Type-Options等,增加一些安全性。
默认创建 HttpSecurity 时会设置该Filter,HttpSecurity.headers(withDefaults())。
public class HeaderWriterFilter extends OncePerRequestFilter {
// 要写入的头信息
private final List<HeaderWriter> headerWriters;
// 默认是false 也就是在过滤器都执行完成后,回到该过滤器时向response中写入
private final boolean shouldWriteHeadersEagerly = false;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 如果是提前写入响应头,则是直接调用了writeHeaders 方法,并继续执行过滤器
if (this.shouldWriteHeadersEagerly) {
doHeadersBefore(request, response, filterChain);
} else {
// 默认走该方法
// 在过滤器执行完成后,再写入头信息
doHeadersAfter(request, response, filterChain);
}
}
}
2.1.6 CsrfFilter
CsrfFilter 是SpringSecurity 中用于防止 CSRF(Cross-site request forgery跨站请求伪造)攻击的过滤器。CSRF攻击的原理是,
当用户已经在某个网站认证完成后浏览器已存储用户的有效信息,攻击者诱导用户登录一个其他的网站,利用用户在原网站未过期的认证信息发起一个非用户本意的请求。
CsrfFilter 的主要作用是验证每个非安全 HTTP 请求(例如 POST、PUT、DELETE 等)中是否包含有效的 CSRF 令牌。如果请求中缺少有效的
CSRF 令牌,CsrfFilter 将拒绝该请求,并返回相应的错误信息。
默认创建 HttpSecurity 时会设置该Filter,HttpSecurity.csrf(withDefaults())。
public final class CsrfFilter extends OncePerRequestFilter {
@Override
private void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
request.setAttribute(HttpServletResponse.class.getName(), response);
// 尝试从存储组件中加载 CsrfToken
CsrfToken csrfToken = this.tokenRepository.loadToken(request);
boolean missingToken = (csrfToken == null);
if (missingToken) {
csrfToken = this.tokenRepository.generateToken(request);
this.tokenRepository.saveToken(csrfToken, request, response);
}
request.setAttribute(CsrfToken.class.getName(), csrfToken);
request.setAttribute(csrfToken.getParameterName(), csrfToken);
if (!this