Spring 框架每个新版本都在修补安全漏洞、淘汰过时 API,而你的项目里那些第三方依赖同样暗藏风险。升级不只是改个版本号——它是一套从评估、迁移到持续监控的完整工程实践。
为什么升级不能再拖
Spring Framework 6.x 和 Spring Boot 3.x 已经是当前主线。停留在 2.x 的项目面临三个现实压力:
- 安全补丁不再回溯——Spring 5.3.x 的维护窗口已收窄,关键 CVE 修复只针对 6.x。
- Jakarta EE 命名空间切换——从
javax.*到jakarta.*是硬性变更,不升级就无法使用新版 Tomcat、Hibernate 等运行时。 - 依赖链断裂——Spring Security 6.x、Spring Data 3.x 等子项目已跟随主线,旧版 Spring Boot 无法搭配新版安全组件。
开源依赖的安全问题同样紧迫。Log4Shell、Spring4Shell 这类事件证明:一个你从未直接调用的传递依赖,可能就是攻击入口。
升级路径:分阶段推进,而非一步到位
第一步:摸清现状
升级前先盘点依赖树和已知漏洞:
# Maven 项目:查看完整依赖树(含传递依赖)
mvn dependency:tree -DoutputType=text > dependency-tree.txt
# 用 OWASP Dependency-Check 扫描已知 CVE
mvn org.owasp:dependency-check-maven:check \
-DfailBuildOnCVSS=7 \
-DsuppressionFile=dep-check-suppressions.xml
failBuildOnCVSS=7 让 CVSS 评分 ≥ 7 的漏洞直接中断构建,迫使你在 CI 阶段就正视高风险问题。suppressionFile 则用于标记你已评估并接受的低风险漏洞,避免噪音干扰。
第二步:从 Spring Boot 2.7 过渡到 3.x
Spring Boot 2.7 是最后一个 2.x 版本,也是官方推荐的跳板。它提供了大量兼容层和迁移提示:
# 先升级到 2.7 最新补丁版
# 在 pom.xml 或 build.gradle 中锁定版本
Maven 示例——锁定 Spring Boot 2.7 并启用迁移提示:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
</parent>
<dependencies>
<!-- 2.7 提供的迁移辅助依赖,会在运行时打印弃用警告 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-properties-migrator</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
运行项目后,控制台会输出哪些配置属性已改名或弃用。逐一修复这些警告,再切到 3.x。
第三步:切换到 Spring Boot 3.x + Jakarta 命名空间
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.5</version>
</parent>
此时必须把所有 javax.* 引用改为 jakarta.*。手动改容易遗漏,用 OpenRewrite 自动化重构更可靠:
<plugin>
<groupId>org.openrewrite.maven</groupId>
<artifactId>rewrite-maven-plugin</artifactId>
<version>5.43.0</version>
<configuration>
<activeRecipes>
<recipe>org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_3</recipe>
</activeRecipes>
</configuration>
</plugin>
执行:
mvn rewrite:run
OpenRewrite 会批量处理 javax→jakarta、弃用 API 替换、配置属性迁移等。跑完后务必跑全量测试,再逐条 review diff。
开源依赖安全:从扫描到治理
升级 Spring 本身解决不了传递依赖的漏洞。你需要一套持续治理流程。
用 Snyk 或 OSV 扫描供应链风险
# Snyk CLI 扫描 Maven 项目
snyk test --file=pom.xml --severity-threshold=high
# 或用 OSV Scanner(开源免费)
osv-scanner scan --lockfile=pom.xml
两者都基于公开漏洞数据库(NVD、GitHub Advisory 等),输出受影响版本和修复建议。Snyk 还能直接提交 PR 升级依赖版本。
锁定依赖版本,杜绝隐式升级
<!-- Maven:用 maven-enforcer-plugin 禁止快照版本和未锁定范围 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.5.0</version>
<executions>
<execution>
<id>enforce-versions</id>
<goals><goal>enforce</goal></goals>
<configuration>
<rules>
<requireReleaseDeps/>
<banDynamicVersions/>
</rules>
</configuration>
</execution>
</executions>
</plugin>
这能防止 1.0-SNAPSHOT 或未指定版本的依赖悄悄进入构建。
CI 中嵌入安全门禁
GitHub Actions 示例——每次 PR 自动跑漏洞扫描:
name: Security Gate
on: [pull_request]
jobs:
dependency-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Run OWASP Dependency-Check
run: |
mvn org.owasp:dependency-check-maven:check \
-DfailBuildOnCVSS=7 \
-DnvdApiKey=${{ secrets.NVD_API_KEY }}
- name: Upload report
if: always()
uses: actions/upload-artifact@v4
with:
name: dep-check-report
path: target/dependency-check-report.html
CVSS ≥ 7 的漏洞会直接让 PR 构建失败,报告上传后可以逐条评审。
升级后的验证清单
升级完成不等于安全落地。跑一遍以下检查:
| 检查项 | 方法 |
|---|---|
| 全量测试通过 | mvn verify 或 gradle build,覆盖率不下降 |
| 无 CVSS ≥ 7 漏洞 | OWASP Dependency-Check 或 Snyk 报告清零 |
javax.* 引用已清空 |
grep -r "import javax" src/ 返回空 |
| 弃用 API 已替换 | 启动日志无 Deprecated 警告 |
| 依赖版本已锁定 | enforcer-plugin 构建通过 |
| 安全门禁已入 CI | PR 触发扫描流水线 |
写在最后
Spring 升级和 OSS 安全不是一次性任务,而是持续工程。核心思路有三条:
- 用 2.7 做跳板,借助
properties-migrator和 OpenRewrite 降低迁移成本。 - 把漏洞扫描嵌入 CI,让高风险依赖在合并前就被拦截。
- 锁定版本、禁止快照,从构建规则层面杜绝供应链漂移。
这三步做完,你的项目才算真正跟上了 Spring 6.x 的安全节奏——而不是在下一个 CVE 公告发布时,才发现自己还在跑两年前的补丁。