樊笼 - Spring Security
type: channel
link: https://252262.xyz/tag/Spring-Security/
items: Spring Security实战教程（十八）安全日志与审计：关键操作追踪与风险预警
items: Spring Security实战教程（十七）企业级安全方案设计 - 多因素认证（MFA）实现
items: Spring Security实战教程（十六）微服务间安全通信 - JWT 令牌传递与校验机制
items: Spring Security实战教程（十五）快速集成 GitHub 与 Gitee 的社交登录
items: Spring Security实战教程（十四）OAuth2.0 精讲 - 四种授权模式与资源服务器搭建
items: Spring Security实战教程（十三）会话管理机制 - 并发控制与会话固定攻击防护
items: Spring Security实战教程（十二）CORS安全配置 - 跨域请求的安全详解
items: Spring Security实战教程（十一）CSRF攻防实战
items: Spring Security实战教程（十）权限表达式进阶：SpEL在安全控制中的高阶魔法
items: Spring Security实战教程（九）前后端分离认证实战 - JWT+SpringSecurity无缝整合

Spring Security实战教程（十八）安全日志与审计：关键操作追踪与风险预警
type: item
date: 2026-03-12T01:30:12+01:00
description: 1. 前言在我们日常开发系统的安全架构中，仅仅通过认证和授权往往不足以满足合规、溯源、风险预警等需求。企业级系统必须记录并分析 “谁在什么时候对什么资源做了什么操作”，并结合规则引擎识别异常行为，及时告警。安全审计作为企业防护的最后一道防线，能有效追踪异常行为、还原攻击链、满足合规要求。本文笔者将带领大家基于 Spring Security 构建完整的安全审计系统，实现关键操作追踪与实时风险预警。2. Spring Security 审计架构设计2.1 系统架构我们常见的安全审计应该具备以下基础：以笔者目前所在公司使用的系统架构如下图：2.2 审计日志四要素要素说明示例主体操作执行者用户 ID、IP 地址客体被操作对象数据 ID、接口路径动作操作类型登录、删除、授权变更环境操作上下文时间、设备、地理位置2.3 技术栈选择事件采集：Spring AOP + ApplicationEvent存储方案：MySQL + MyBatis‑Plus（中小型应用适用）或 Elasticsearch + Logstash（大型应用 + 实时检索）风险分析：Drools 规则引擎可视化：Grafana 监控看板笔者为简化演示，本文以 MySQL + MyBatis‑Plus 为存储方案。鉴于之前的子模块代码中，我们已经集成好了 MySQL + MyBatis‑Plus，在开始之前复用之前的子模块，需在 pom.xml 中引入：<dependency> <groupId>org.drools</groupId> <artifactId>drools-core</artifactId> <version>9.44.0.Final</version> </dependency> <dependency> <groupId>org.kie</groupId> <artifactId>kie-spring</artifactId> <version>7.74.1.Final</version> </dependency>3. 数据库与实体设计CREATE TABLE audit_log ( id BIGINT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50), action VARCHAR(100), resource VARCHAR(200), timestamp DATETIME, duration BIGINT );@Data @TableName("audit_log") public class AuditLog { @TableId(type = IdType.AUTO) private Long id; private String username; private String action; private String resource; private LocalDateTime timestamp; private Long duration; } // Mapper @Mapper public interface AuditLogMapper extends BaseMapper<AuditLog> {}4. 事件采集：Spring AOP + 自定义注解4.1 自定义注解@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface Audit { String action(); String resource(); }4.2 AOP 切面实现@Aspect @Component public class AuditAspect { @Autowired private ApplicationEventPublisher publisher; @Pointcut("@annotation(audit)") public void auditPoint(Audit audit) {} @Around("auditPoint(audit)") public Object around(ProceedingJoinPoint jp, Audit audit) throws Throwable { long start = System.currentTimeMillis(); Object result = jp.proceed(); long duration = System.currentTimeMillis() - start; MethodSignature ms = (MethodSignature) jp.getSignature(); String userna…

Spring Security实战教程（十七）企业级安全方案设计 - 多因素认证（MFA）实现
type: item
date: 2026-03-12T01:28:00+01:00
description: 1. 前言在微服务与分布式架构日益普及的今天，传统的单一凭证（用户名+密码） 已经难以满足企业对于身份验证的高安全性需求。多因素认证（Multi‑Factor Authentication，简称 MFA） 通过“用户知道的东西”（如密码）+“用户拥有的东西”（如动态验证码）或“用户自身的一部分”（如指纹）三种因素的组合，大幅提升了系统防护能力。比如我们常见的 GitHub、腾讯云等就开启了 MFA。GitHub 开启 MFA 后可以使用 Authenticator 应用扫描，而腾讯云则需要短信验证码来进行校验。本章节笔者将带领大家深入解析 MFA，并基于 Spring Security 6，结合 MySQL 与 MyBatis-Plus，从理论到实战，快速构建一套企业级的 MFA 认证方案。2. 为什么需要多因素认证？传统认证的风险密码脆弱性：大部分的数据泄露源于弱密码或重复密码。撞库攻击：黑客利用泄露的密码库尝试登录其他系统。钓鱼攻击：伪造登录页面窃取用户凭证。MFA 的核心优势多因素认证（MFA）通过多种不同类别的凭证来共同完成身份验证，显著提升安全性：Something you know（你知道的东西）：用户名与密码、PIN 码等；Something you have（你拥有的东西）：手机收到的 OTP、应用令牌（Authenticator）等；Something you are（你自身的一部分）：生物特征（指纹、面部识别等）。当密码被破解或泄露后，如果没有第二因素（如手机动态验证码），攻击者依然无法登录。常见多因素认证实现方案认证方式安全性用户体验实施成本短信验证码★★★★★★☆★★★TOTP（如 Google Authenticator）★★★☆★★★☆★★☆生物识别★★★☆★★★★★★★★本方案选择 TOTP：平衡安全性与实施成本，兼容 Google Authenticator 等标准应用。3. 多因素认证的核心原理以 TOTP（Time‑based One‑Time Password） 为例：服务端生成用户专属密钥（Secret Key），并在用户首次登录或在安全设置中心将其展示给用户（通常通过二维码形式扫描到 Google Authenticator、Authy 等应用中）。手机端应用（如 Google Authenticator）基于 Secret Key 与当前时间戳，通过 HMAC‑SHA1 算法计算出 6 位动态验证码。用户登录时，输入用户名+密码（第 1 因素），若校验通过，跳转到 MFA 验证页面，要求输入手机上展示的 6 位动态验证码（第 2 因素）。服务端验证客户端提交的动态验证码是否与基于相同 Secret Key 和当前时间戳计算出的值一致。若一致，则认为通过 MFA，登录成功；否则，拒绝登录或提示重试。整个流程中，只有用户掌握 Secret Key（存在手机应用中），且需实时生成动态验证码。即使攻击者获得了用户名+密码，没有手机和 Secret Key，也无法通过第二因素验证。4. 系统架构与流程设计本章节以单体 Spring Boot 应用演示 MFA 流程，生产环境可拆分成独立的认证服务（Auth Service）与业务服务（Resource Service），二者均依赖集中管理的用户与 MFA 数据库。关键流程：❶ MFA 初始化后台管理员或用户注册时，系统为用户生成一对 RSA 密钥（可选）或仅生成 TOTP Secret，保存至用户表中。将生成的 Secret 以二维码或明文形式呈现给用户，用户通过 Google Authenticator 等扫描或手动录入。❷ 第一步：用户名+密码登录用户提交用户名+密码，Service 层校验密码（结合 BCrypt）。校验成功后，将用户标记为“已通过第一步认证”，并生成一个短期令牌（可存放到 session 或 JWT）表示“待 MFA”状态，重定向到 MFA 验证页。❸ TOTP 验证用户在 MFA 验证页中输入 6 位动态验证码，提交后，后台从数据库中取出该用户的 Secret，通过 TOTP 算法生成当前时刻的合法验证码，进行比对。若校验通过，则完成整个登录流程，Spring Security 将真正的 Authentication 对象置入 SecurityContext 中，登录成功，跳转到首页；否则，提示错误并重试。❹ 完整流程图5. Spring Security 整合 MFA 实现根据前面的章节我们已经整合好了 mysql + mybatis 等的项目案例，我们继续追加子模块，引入 Google Authenticator 兼容 TOTP 实现：com.warrenstrange:googleauth:1.5.0。5.1 引…

Spring Security实战教程（十六）微服务间安全通信 - JWT 令牌传递与校验机制
type: item
date: 2026-03-12T01:25:00+01:00
description: 1. 前言在微服务架构中，服务间的安全通信至关重要。为了解决不同服务之间的认证与授权问题，常常使用 JSON Web Token (JWT) 作为令牌传递机制。JWT 是一种轻量级的令牌格式，包含丰富的用户身份信息，并且可以被服务端验证。利用 Spring Security 6 和 JWT，我们可以轻松实现服务间的安全通信，确保只有经过授权的客户端才能发起服务间请求。在前面的第 9 章节中，笔者详细介绍了 JWT + Spring Security 的整合，本文仅对 JWT 进行简要介绍。更多详细内容感兴趣的小伙伴可回顾 Spring Security 实战教程（九）前后端分离认证实战 - JWT+SpringSecurity 无缝整合。本文将带领大家快速了解如何使用 Spring Security 6 配合 JWT 实现服务间的安全通信。微服务间 JWT 通信核心流程2. 配置 Spring Security 6 与 Nimbus-JOSE-JWT本章节笔者使用 Nimbus-JOSE-JWT 作为 JWT 处理库。2.1 引入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-resource-server</artifactId> </dependency> <dependency> <groupId>com.nimbusds</groupId> <artifactId>nimbus-jose-jwt</artifactId> <version>9.37</version> </dependency>spring-boot-starter-oauth2-resource-server 提供了 Spring Security 中资源服务器的支持。nimbus-jose-jwt 提供了生成和验证 JWT 的功能。关键依赖说明依赖项作用必需场景spring-boot-starter-security安全框架核心所有安全场景必须spring-boot-starter-oauth2-resource-serverOAuth2 资源服务器支持JWT 验证必需nimbus-jose-jwtJWT 编码/解码实现JWT 处理必需2.2 配置公钥和私钥为了使用 RSA 进行 JWT 签名和验证，我们需要配置私钥（用于签名）和公钥（用于验证）。可以将密钥存储为 PEM 格式的文件，并在 Spring Boot 配置中加载这些密钥。生成公私钥对首先，生成一个 RSA 公私钥对：# 生成私钥（private.pem） openssl genpkey -algorithm RSA -out private.pem -pkeyopt rsa_keygen_bits:2048 # 提取公钥（public.pem） openssl rsa -pubout -in private.pem -out public.pem将密钥加载到 Spring Boot将私钥和公钥存储为文件，并在配置中加载它们：# application.yml 配置公钥和私钥路径 spring: security: oauth2: resourceserver: jwt: public-key-location: classpath:public.pem private-key-location: classpath:private.pem此处我们将密钥文件放在 resources 目录下，并通过配置 public-key-location 和 private-key-location 来加载它们。也可指定系统上的其他目录3. JWT 生成和验证3.1 生成 JWT 令牌创建一个方法，通过 RSA 私钥签名 JWT 令牌。使用 Nimbus-JOSE-JWT 生成带有签名的 JWT。import com.nimbusds.jose.*; import com.nimbusds.jose.crypto.RSASSASigner; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; import java.security.PrivateKey; import java.util.Date; public class JwtUtils { private final PrivateKey privateKey; public JwtUtils(PrivateKey privateKey) { this.privateKey = privateKey; …

Spring Security实战教程（十五）快速集成 GitHub 与 Gitee 的社交登录
type: item
date: 2026-03-12T01:24:28+01:00
description: 1. 前言在微服务与前后端分离架构中，第三方社交登录已成为提升用户体验的重要功能。社交登录可以有效降低用户注册成本，同时利用第三方平台的账号体系，实现快速认证与信息获取。Spring Security 6 作为 Java 生态中的安全框架，通过 OAuth2 协议简化了第三方认证的集成流程。本章节笔者将通过完整代码案例，讲解如何基于 Spring Security 6 实现 GitHub 和 Gitee 的社交登录功能。2. 原理分析回顾上一章节介绍的 OAuth 2.0 授权码流程（最新 Spring Security 实战教程（十四）OAuth2.0 精讲 - 四种授权模式与资源服务器搭建）：其主要流程包括：跳转授权：客户端（我们的网站）将用户重定向至第三方授权服务器的授权端点，携带 client_id、redirect_uri、scope 等参数。用户登录并同意：用户在第三方平台完成登录后，同意授权给客户端指定权限。回调获取授权码：授权服务器重定向回客户端注册的 redirect_uri，并在查询参数中附带 code。交换令牌：客户端后端使用 code、client_secret 等向授权服务器的令牌端点发起 POST 请求，获取 access_token。获取用户信息：客户端携带 access_token 调用用户信息端点，解析并登录或注册用户。3. 开发准备3.1 初始化项目继续基于之前的项目构建子项目 social-login-spring-security，pom 文件引入项目依赖：<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-client</artifactId> </dependency> </dependencies>GitHub/Gitee 开发者账号：用于注册 OAuth 应用。3.2 注册 GitHub OAuth 应用访问 GitHub 设置 → Developer settings → OAuth Apps → New OAuth App，填写应用名、主页 URL、授权回调 URL。填写应用信息：默认的重定向 URI 模板为 {baseUrl}/login/oauth2/code/{registrationId}。registrationId 是 ClientRegistration 的唯一标识符。例如 GitHub 固定写法则为：{baseUrl}/login/oauth2/code/github。获取应用程序 ID，生成应用程序密钥：保存好生成的 Client ID 与 Client Secret。3.3 注册 Gitee OAuth 应用登录 Gitee，访问 Gitee 应用管理，创建新应用。提交创建应用后会得到 Client ID 和 Client Secret。4. 实战案例完成上述准备工作后，我们开始对项目进行配置。4.1 配置 application.yml# 最新 Spring Security 实战教程（十五）快速集成 GitHub 与 Gitee 的社交登录 spring: application: name: social-login-spring-security security: oauth2: client: registration: github: client-id: YOUR_CLIENT_ID client-secret: YOUR_CLIENT_SECRET scope: user:email redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}" authorization-grant-type: authorization_code client-name: github gitee: client-id: YOUR_CLIENT_ID client-secret: YOUR_CLIENT_SECRET scope: user_info client-name: Gitee authorization-grant-type: authorization_code redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}" provider: gitee provider: gitee: authorization-uri: https://gitee.com/oauth/authorize token-uri: https://gitee.com/oa…

Spring Security实战教程（十四）OAuth2.0 精讲 - 四种授权模式与资源服务器搭建
type: item
date: 2026-03-12T01:20:30+01:00
description: 1. 前言（OAuth2 简介）目前 OAuth2.0 已成为现代应用认证授权的标准，从单点登录到微服务架构都依赖其构建安全通道。我们常见的微信、QQ、微博等应用均有使用。OAuth 2.0（Open Authorization）是一种开放的授权框架，允许应用在不暴露用户密码的前提下，安全地代表用户访问第三方服务上的受保护资源。本章节，笔者将带领大家深入解析四种核心授权模式，并基于 Spring Security 6 手把手搭建安全的资源服务器，助您掌握分布式系统的认证精髓。注：部分介绍参考了官方文档：https://docs.spring.io/spring-security/reference/servlet/oauth2/index.html，小伙伴们可自行阅读并查阅官方示例代码。2. OAuth2 的角色根据 RFC 6749 标准，OAuth 2.0 定义了四种角色，每个角色在授权流程中承担不同职责：资源所有者（Resource Owner）：通常是终端用户，拥有受保护资源。客户端（Client）：向用户请求授权，代表用户访问资源的应用，通常是一个 Web 或移动应用。授权服务器（Authorization Server）：认证资源所有者身份并颁发访问令牌。资源服务器（Resource Server）：托管资源，根据令牌的有效性和权限范围决定是否允许访问。为帮助小伙伴们更好理解，笔者用一个现实生活中的场景（小区门禁系统）来类比 OAuth2.0 的工作原理，直观理解其角色与四种授权模式。场景：小区门禁系统假设你住在一个需要刷卡或扫码进入的小区，门禁系统要求验证身份后才能开门。以下是 OAuth2.0 的四个角色映射：资源所有者（Resource Owner）：住户（你），你是小区住户，拥有进出小区的权限。客户端（Client）：快递员或访客，需要临时进入小区的人或设备（如快递员的小程序）。授权服务器（Authorization Server）：物业中心，负责验证住户身份，并发放临时通行权限（令牌）。资源服务器（Resource Server）：小区门禁闸机，接收令牌（如二维码或门禁卡），验证后决定是否放行。OAuth2 的核心逻辑你（住户）不想直接给快递员门禁卡或密码，而是通过物业中心生成一个临时二维码（令牌），限制其权限（例如：仅允许进入 1 次，或仅在 30 分钟内有效）。令牌由物业中心发放，你可以随时在物业系统中吊销它。3. OAuth 2.0 四种授权模式不同场景下，OAuth 2.0 定义了四种核心授权模式，各有适用范围和安全考量。3.1 授权码模式（Authorization Code Grant）授权码模式，指第三方应用先申请一个授权码，然后再用该码获取令牌。 这种方式最常用、最复杂，也是最安全的，适用于那些有后端的 Web 应用。授权码通过前端传送，令牌则储存在后端，且所有与资源服务器的通信都在后端完成。这样的前后端分离，可避免令牌泄漏。场景：适用于安全的服务端 Web 应用，例如电商网站在结算时需要访问支付平台 API。流程：客户端引导用户浏览器跳转到授权服务器授权端点；用户登录并同意后，授权服务器重定向携带 code 到客户端回调 URL；客户端使用该 code 向令牌端点申请访问令牌，并在请求中携带客户端凭证。优点：令牌通过后端交换，避免在浏览器中暴露，安全性较高。现实生活模拟场景 还是以小区门禁系统为例：快递员通过「小区快递 App」申请进入权限。快递员点击 App「申请进入小区」，App 跳转到物业的授权页面。你（住户）在物业 App 上接收到授权通知，并同意授权（例如：允许快递员在 30 分钟内进入小区 1 次）。物业中心生成一个授权码返回给快递员的快递 App。快递 App 用授权码向物业中心换取一个临时二维码（令牌）。快递员用二维码扫码进入小区门禁（资源服务器）。3.2 隐式模式（Implicit Grant）场景：传统的单页应用（SPA）或无法安全存储客户端密钥的纯前端应用。流程：用户同意授权后，授权服务器直接在重定向 URI 的 fragment 中返回访问令牌，无需第二次交换。缺点：令牌暴露在浏览器，易受截取攻击。注：Spring Security 6 已移除隐式模式支持，建议使用 PKCE 增强的授权码模式。// Spring Security 6 已移除隐式模式支持 http.oauth2Client(oauth2 -> oauth2 .authorizationCodeGrant(Customizer.withDefaults()) )现实生活模拟场景 访客临时申请进入小区：访客在门禁屏幕上点击「申请临时通行」。直接跳转到物业授权页面，你登录并同意授权。物业中心直接返回一个…

Spring Security实战教程（十三）会话管理机制 - 并发控制与会话固定攻击防护
type: item
date: 2026-03-12T01:16:39+01:00
description: 1. 前言在 Web 应用安全体系中，会话管理是认证授权后的重要防线。攻击者常通过会话劫持与会话固定突破系统边界，而业务系统则面临并发滥用带来的资源风险。Spring Security 的会话管理模块由 SessionManagementFilter 与一系列 SessionAuthenticationStrategy 共同协作，负责在用户登录或访问受保护资源时执行统一的会话检查与策略。默认情况下，框架允许单个用户拥有无限多个并发会话，而在每次登录时会执行会话固定保护策略，将旧 Session ID 迁移到新 Session 中，以防止攻击者利用已有的 Session ID 进行劫持。在本章节，笔者将基于 Spring Security 6，深入解析会话管理的安全实践。2. 会话固定攻击防护原理2.1 攻击概述会话固定（Session Fixation）指攻击者诱导受害者在已知的 Session ID 下登录，随后持该 ID 进行未授权操作。Spring Security 通过会话固定保护策略，在每次用户登录后刷新 Session ID 来防御此类攻击。攻击流程解析2.2 Spring Security 防御策略SessionManagementFilter 在认证成功后，会调用相应的 SessionAuthenticationStrategy 执行上述策略。migrateSession() 在底层通过 HttpServletRequest.changeSessionId() 或复制属性到新 Session 来实现 ID 刷新，有效防止攻击者使用固定 ID 进行入侵。配置样例@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http // 启用会话固定防护，采用迁移会话策略 .sessionManagement(session -> session .sessionFixation(fix -> fix.migrateSession() // 默认：保留旧数据，生成新 Session ID // fix.changeSessionId() // 仅变更会话 ID // fix.newSession() // 完全新建 Session，不保留任何属性 // fix.none() // 禁用保护（不推荐） ) ) .authorizeHttpRequests(auth -> auth .anyRequest().authenticated() ); return http.build(); } }配置说明none：不做任何处理，不推荐使用changeSessionId：认证后仅变更会话 IDnewSession：创建全新、干净的 Session，不复制原属性migrateSession（默认）：创建新 Session 并复制原有属性，保留必要数据同时更换 ID，防范攻击者利用旧 ID3. 并发会话控制方案当同一账号在多处登录时，可能带来数据冲突、授权混乱甚至资源浪费。Spring Security 通过并发会话控制，可限制用户的最大在线会话数，并在超过限制时选择“踢出旧会话”或“拒绝新登录”两种策略。3.1 为什么要限制并发会话？防止账号共享：同一账号在多处登录可能意味着凭证泄露防范会话劫持：一旦旧会话被劫持，可通过限制数量自动踢出旧会话3.2 核心组件SessionRegistry：维护用户与其活跃 Session 的映射ConcurrentSessionControlAuthenticationStrategy：在用户登录时检查并发会话数量，超过上限可抛出 SessionAuthenticationException 或踢出最早会话HttpSessionEventPublisher：侦听 HttpSessionEvent，在 Session 销毁时同步更新 SessionRegistry3.3 配置样例@Configuration @EnableWebSecurity public class SecurityConfig { // 1. 注册 Session 事件监听器，使 Spring Security 能及时感知会话生命周期事件 @Bean public HttpSessionEventPublisher httpSessionEventPublisher() { return new HttpSessionEventPublisher(); } @Bean pub…

Spring Security实战教程（十二）CORS安全配置 - 跨域请求的安全详解
type: item
date: 2026-03-12T01:04:47+01:00
description: 1. 前言在日常开发中，跨域资源共享（CORS）已成为前端与后端分离架构的必备能力。正确配置 CORS 是后端安全边界的重要一环。但安全与便利的天平往往难以把握——过于宽松的配置可能导致敏感数据泄露，过于严格的策略又会影响正常业务交互。本文我们继续《Spring Security 实战教程》，讲解 CORS 安全配置。2. 背景与原理概述跨域请求是指浏览器在一个域（Origin A）加载的脚本访问另一个域（Origin B）的资源。为保护用户数据，浏览器默认禁止跨域访问，除非服务器在响应头中明确允许。CORS 机制通过 Access-Control-Allow-* 系列头部，告诉浏览器何种跨域请求被允许，从而实现安全的跨域通信。2.1 预检请求全流程当发起“简单请求”时（如 GET/POST 且只有简单头部），浏览器会直接带上 Cookie 等凭证；对复杂请求（如 PUT/DELETE、携带自定义头部）则先发起一次预检（OPTIONS）请求，确认服务器同意后才发送实际请求。 2.2 关键响应头说明Access-Control-Allow-* 响应头主要包括以下几个：响应头安全意义示例值Access-Control-Allow-Origin允许的源列表https://your-domain.comAccess-Control-Allow-Methods允许的HTTP方法GET,POST,PUTAccess-Control-Allow-Headers允许的请求头Content-Type,AuthorizationAccess-Control-Max-Age预检结果缓存时间（秒）864003. Spring MVC 中的 CORS 支持Spring Framework 自 4.2 起在 MVC 层面引入了对 CORS 的原生支持，可通过全局配置或注解方式开启。全局配置（WebMvcConfigurer）@Configuration public class CorsGlobalConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") // 应用到所有 /api 路径 .allowedOrigins("https://your-domain.com") // 允许的源 .allowedMethods("GET", "POST", "PUT") // 允许的方法 .allowedHeaders("Content-Type", "Authorization") .exposedHeaders("X-Total-Count") .allowCredentials(true) // 允许携带 Cookie .maxAge(3600); // 预检结果缓存 1 小时 } }注解方式（@CrossOrigin）@RestController @RequestMapping("/users") @CrossOrigin( origins = "https://your-domain.com", methods = {RequestMethod.GET, RequestMethod.POST}, allowCredentials = "true", maxAge = 1800 ) public class UserController { @GetMapping public List<User> list() { … } }4. Spring Security 安全配置方案尽管 Spring MVC 已处理了 CORS，但如果启用了 Spring Security，必须在 Spring Security 配置中显式开启 CORS 支持，否则安全过滤器会在 MVC 之前拦截预检请求，导致跨域失败。全局安全配置@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .cors(cors -> cors .configurationSource(corsConfigurationSource()) ) .authorizeHttpRequests(auth -> auth .anyRequest().authenticated() ); // 其他安全配置... return http.buil…

Spring Security实战教程（十一）CSRF攻防实战
type: item
date: 2026-03-12T01:03:00+01:00
description: 1. 前言在前面学习的章节中，相信大家一定看过一个配置.csrf()。回忆一下之前使用Spring Security默认页登录的时候，该配置Spring Security默认开启，主要用于CSRF防护。如果你现在还不了解什么是CSRF防护，没关系，通过本章节，博主带着大家一起深入学习这个知识点～2. CSRF攻击原理跨站请求伪造（CSRF）是一种利用受信任用户的身份，诱使用户在已登录的应用中执行非预期操作的攻击手段。当用户在某个站点（如银行）登录并持有有效Session Cookie后，攻击者可通过精心构造的请求（例如隐藏在图片或表单中的POST请求）在用户不知情的情况下向该站点发起请求，并携带用户的Cookie，从而完成诸如转账、修改邮箱等敏感操作。2.1 攻击原理图解用户访问了A站点，获得了Session或Cookie后，用户不经意间访问到了恶意网站，此刻恶意网站伪造对A站点的危险请求2.2 攻击示例下面示例展示了一个最常见的CSRF攻击场景：用户登录了https://bank.com后，攻击者在自己的网站https://evil.com上放置如下HTML片段： 3. Spring Security防御机制解析3.1 同步令牌模式（Synchronizer Token Pattern）同步令牌模式是Spring Security的默认方案，服务器在渲染每个需要保护的表单页面时，向用户Session中存入一个随机生成的Token，并在表单中以隐藏字段输出；提交时，服务器验证该字段与Session中的Token是否一致，若不匹配则拒绝请求。此模式能有效防止CSRF攻击，因为攻击者无法从第三方域读取到该随机Token。核心防御流程服务端生成随机Token（每个Session唯一）Token嵌入HTML表单的隐藏字段或HTTP头客户端提交请求时必须携带有效Token服务端校验Token合法性3.2 双重提交Cookie（Double Submit Cookie）服务器在首次响应页面时，通过Set-Cookie设置一个随机Token，同时在页面中通过脚本将该Token读出并写入一个请求头（或隐藏表单字段）。服务器接收请求后，比较Cookie中的Token与请求中携带的Token是否一致。由于浏览器同源策略不能让第三方域读取Cookie，攻击者无法同步两个值。3.3 SameSite Cookie属性浏览器支持在Set-Cookie响应头中声明SameSite属性，用来限制Cookie在跨站请求时是否发送。设置为Strict或Lax模式，可从源头上阻止大部分CSRF请求。SameSite=Strict：绝不在第三方请求中发送该Cookie；SameSite=Lax：仅允许在“安全”的跨站GET导航中发送。4. 实战代码示例这里我们将针对上述三种防护机制，进行相关代码演示。4.1 在Spring Security中启用CSRF防护Spring Security默认开启CSRF保护，采用的是同步令牌模式。下面展示如何在单体、前后分离中集成。❶ Thymeleaf模板中集成@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf // 可自定义CsrfTokenRepository，例如 CookieCsrfTokenRepository.withHttpOnlyFalse() ) .authorizeHttpRequests(auth -> auth .anyRequest().authenticated() ) .formLogin(withDefaults()); return http.build(); } }在Thymeleaf页面中添加隐藏字段，设置Token： Transfer 在控制器中，Spring Security自动会在每次POST请求时校验表单中的${_csrf.token}与Session中的令牌是否匹配，不匹配则抛出InvalidCsrfTokenException。❷ 前后端分离适配方案下面演示在前后分离中的适配，前端在请求前初始化时获取CSRF Token：// 自定义CSRF令牌处理器 public class SpaCsrfTokenRequestHandler extends CsrfTokenRequestAttributeHandler { @Override public void handle(HttpServlet…

Spring Security实战教程（十）权限表达式进阶：SpEL在安全控制中的高阶魔法
type: item
date: 2026-03-12T01:02:44+01:00
description: title: （十）权限表达式进阶：SpEL在安全控制中的高阶魔法categories: [程技]tags: [Spring Security]date: 2026-03-021. 前言博主在持续更新Spring Security教程过程中，有一些小伙伴总会私信问到之前关于ABAC属性权限模型那一个章节中自定义策略条件表达式的问题，如下图：温馨提示：没了解ABAC属性权限模型的可以访问：最新Spring Security实战教程（六）基于数据库的ABAC属性权限模型实战开发进行了解。大家最关心的问题就是：为什么在EvaluationContext上下文中传入了对应的自定义策略条件表达式，Spring Security就能进行更细粒度控制？针对这些问题，博主觉得还是有必要再补充一下SpEL在Spring Security中的一些应用技巧。2. SpEL在Spring Security中的基本应用Spring Expression Language（SpEL）作为Spring生态的通用表达式引擎，在Spring Security中扮演着权限控制的灵魂角色。通过SpEL表达式让权限判断更灵活、更具有可配置性。我们不仅可以对用户角色进行细粒度控制，还可以在方法参数、请求信息等多维度上做出动态判断，满足复杂业务场景下的安全需求。2.1 基础权限校验在Spring Security的注解中，经常会看到@PreAuthorize、@PostAuthorize、@PreFilter、@PostFilter等注解，实际上这些注解内部都使用了SpEL来编写权限表达式：@PreAuthorize("hasAuthority('USER_CREATE')") public void createUser(User user) { /*...*/ } @PreAuthorize("hasRole('ADMIN') or hasRole('AUDITOR')") public void auditSystem() { /*...*/ }在上面的例子中，hasRole('ADMIN')就是基于SpEL的表达式。Spring Security内部会将该表达式编译并执行，根据认证对象中的角色信息决定是否允许访问。2.2 复杂对象属性校验下面举例：Project.owner（项目所有者必须是当前认证用户并且项目状态 = EDITABLE）：public record Project(Long id, String owner, String status) {} // 校验项目所有者且项目状态为可编辑 @PreAuthorize("#project.owner == authentication.name and #project.status == 'EDITABLE'") public void updateProject(Project project) { // 更新逻辑 }2.3 方法参数动态处理下面再看看方法上的动态处理：// 校验用户ID与当前认证用户匹配 @PreAuthorize("#userId == authentication.principal.id") public User getUserProfile(Long userId) { return userService.findById(userId); } // 集合参数过滤 @PostFilter("filterObject.department == authentication.principal.department") public List<Document> getAllDocuments() { return documentRepository.findAll(); }通过上述的几个例子，就可以知道Spring Security常见的几个注解就是基于SpEL的表达式。3. 常用的SpEL表达式及变量在Spring Security中，SpEL表达式可以访问以下几个常见的内置变量及方法，从而实现灵活的权限控制：❶ authentication获取当前用户的身份认证信息，包含用户名、密码、授权信息等。示例：// 判断传递用户名是否当前认证信息中的用户 @PreAuthorize("authentication.principal.username == #username") public void getUserDetails(String username) { /*...*/ }❷ principal当前经过认证的用户对象，通常是一个实现了UserDetails接口的对象。示例：// 判断当前用户是否启用 @PreAuthorize("principal.en…

Spring Security实战教程（九）前后端分离认证实战 - JWT+SpringSecurity无缝整合
type: item
date: 2026-03-12T01:02:08+01:00
description: title: （九）前后端分离认证实战 - JWT+SpringSecurity无缝整合categories: [程技]tags: [spring security]date: 2026-03-021. 前言又是新的一周，博主继续来给大家更新 Spring Security 实战教程系列啦～ 通过前面的章节教程从认证到授权，相信大家已经基本了解 Spring Security 的工作原理。但在前后端分离架构成为主流的今天，传统的 Session-Cookie 认证模式面临跨域限制、服务端状态维护等难题。JWT（JSON Web Token）作为无状态令牌方案，凭借其自包含、易扩展的特性，成为现代分布式系统的首选认证方案。那么本章节，博主就带着大家一起来进行 Spring Security 前后端分离认证实战，手把手教构建安全的 JWT 认证体系！2. JWT 基本原理JWT（JSON Web Token） 是一种开放标准（RFC 7519），用于在各方之间以 JSON 对象安全地传输信息。其主要特点包括：无状态：服务端无需保存会话信息，降低了服务端压力（传统 Session 是保存服务端）跨域支持：适用于前后端分离应用场景JWT 通常由三部分组成：Header、Payload 和 Signature。在认证场景中，用户登录后服务器生成一个包含用户信息的 Token，前端将该 Token 存储在本地，并在后续请求中携带到 HTTP Header 中。服务端通过解析和验证 Token，完成用户身份认证。3. Spring Security 与 JWT 整合思路整合 JWT 与 Spring Security 的关键在于：无状态配置：关闭 Spring Security 默认的 Session 管理，采用无状态认证自定义认证入口：提供一个登录接口，验证用户凭据，生成 JWTJWT 拦截过滤器：在请求到达业务逻辑前，拦截 HTTP 请求，解析和验证 JWT，将用户认证信息写入 SecurityContext完整流程图如下：4. 实战开始本次演示我们先简单模拟集成，还是在之前的 Maven 项目中新建子模块，命名：jwt-spring-security Maven 配置文件追加 JWT 库：<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.12.6</version> </dependency>YML 配置文件中添加密钥以及 Token 有效期：jwt: secret: "dGhpcyBpcyBhIHNlY3JldCBrZXkgZm9yIG15IGFwcA==" # Base64编码密钥 expiration: 900000 # 15分钟4.1 配置 JWT 工具类如果你的系统使用了 Hutool 工具包，可以直接调用 JWTUtil 来创建以及验证 JWT，具体参考 https://doc.hutool.cn/pages/JWTUtil/这里我们使用的是 io.jsonwebtoken 来自定义 JWT：@Component public class JwtUtils { @Value("${jwt.secret}") private String secret; @Value("${jwt.expiration}") private long expiration; // 生成令牌时设置 subject public String generateToken(String username) { return Jwts.builder() .subject(username) // 关键：设置用户名到 subject .issuedAt(new Date()) .expiration(new Date(System.currentTimeMillis() + expiration)) .signWith(getSigningKey()) .compact(); } // 用户名提取方法 public String extractUsername(String token) { return parseClaims(token).getSubject(); } // 统一的令牌验证方法 public boolean validateToken(String token) { parseClaims(token); // 复用解析逻辑 return true; } // 校验 Token 是否过期 public boolean isTokenExpired(String token) { Claims clai…