Appearance
CAS Gradle 9.x 与 JDK 21 构建体系升级:从 Toolchains 到 CycloneDX SBOM 的全链路实践
作者: 必码 | bima.cc
前言
Apereo CAS(Central Authentication Service)作为全球最广泛采用的开源单点登录(SSO)解决方案之一,其构建体系在 7.3 版本中经历了自项目创立以来最为深刻的变革。CAS 7.3 基于 Spring Boot 3.5.6 和 Java 21 构建,这意味着它不仅要求运行时环境升级到 JDK 21,更在构建工具链层面全面拥抱了 Gradle 9.x 的新特性。从 Gradle Toolchains 的自动 JDK 下载机制,到 CycloneDX SBOM 的软件物料清单生成,再到 Jib 3.5.3 的多平台容器镜像构建,整个构建体系发生了质的变化。
本文基于 cas-overlay 项目 7.3 版本的 Gradle 9.1.0 构建配置,从源码分析的角度出发,系统性地梳理 CAS 构建体系从 5.3 到 7.3 的演进路径,深入解析每一个关键配置变更背后的技术动因。我们不仅会看到"改了什么",更要理解"为什么改"以及"怎么改"。
本文的读者受众包括:
- 负责将 CAS 从 6.x 或 5.x 升级到 7.3 的架构师和运维工程师
- 需要理解 Gradle 9.x 新特性对现有构建脚本影响的构建工程师
- 关注软件供应链安全(SBOM)的技术管理者
- 希望了解 Java 21 对 CAS 容器化部署影响的 DevOps 工程师
- 对 CAS 依赖管理策略感兴趣的后端开发者
本文将从七个维度展开论述:构建体系跨版本演进全景、Gradle 9.x 核心特性与 CAS 适配、Foojay Toolchains 插件深度解析、CycloneDX SBOM 生成、Jib 多平台 Docker 镜像构建、Docker 多阶段构建 JDK 21 适配,以及依赖管理与版本控制。每个章节都包含可落地的配置示例和版本对比分析,力求为读者提供一份完整的升级参考手册。
第一章 CAS 构建体系跨版本演进全景
1.1 CAS 5.3:Gradle 7.5 + Java 8
CAS 5.3 是一个具有里程碑意义的版本,它标志着 CAS 项目从 Gradle 4.x 向 Gradle 7.5 的重大跨越。在 CAS 5.3 时代,Java 8 仍然是企业级应用的主流运行时环境,整个构建体系围绕 Java 8 的生态展开。
核心构建配置特征:
groovy
// CAS 5.3 典型构建配置(简化版)
plugins {
id 'java'
id 'org.springframework.boot' version '2.7.x'
id 'io.spring.dependency-management' version '1.1.x'
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
dependencies {
// MyBatis 数据层
implementation 'org.mybatis:mybatis:3.5.6'
implementation 'org.mybatis:mybatis-spring:1.3.1'
// 数据库连接池
implementation 'commons-dbcp:commons-dbcp:1.4'
// 前端资源
implementation 'org.webjars:bootstrap:3.4.1'
implementation 'org.webjars:jquery:3.6.0'
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
CAS 5.3 的构建体系具有以下显著特征:
Java 版本硬编码:通过
sourceCompatibility和targetCompatibility直接指定 Java 8,构建时要求JAVA_HOME指向 JDK 8 安装目录。这意味着开发团队的每个成员都必须手动安装并配置正确版本的 JDK。依赖管理简单直接:使用
io.spring.dependency-management插件配合 BOM(Bill of Materials)进行版本控制,依赖声明方式以implementation为主,版本号在大多数情况下由 BOM 隐式管理。MyBatis 生态的"保守选择":MyBatis 3.5.6 配合 mybatis-spring 1.3.1,这是一个经过大量生产验证的组合。mybatis-spring 1.3.1 对 Spring Framework 5.x 提供了良好的支持,但缺少对 Spring Boot 2.x 自动配置的原生集成。
连接池的"经典方案":使用 Apache Commons DBCP 1.4 作为数据库连接池。DBCP 1.4 虽然稳定,但缺乏连接池监控、异步获取连接等现代特性。
前端资源管理:WebJars 版本相对保守,Bootstrap 3.4.1 和 jQuery 3.6.0 是当时的主流选择。
CAS 5.3 构建体系架构图:
┌─────────────────────────────────────────────────────────────┐
│ CAS 5.3 构建体系 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌───────────────┐ │
│ │ Gradle 7.5 │───▶│ Java 8 JDK │───▶│ CAS WAR 包 │ │
│ │ (构建工具) │ │ (手动安装) │ │ (Servlet 3.1)│ │
│ └─────────────┘ └──────────────┘ └───────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────┐ ┌───────────────┐ │
│ │ Spring Boot │ │ Docker 镜像 │ │
│ │ 2.7.x │ │ (手动构建) │ │
│ └─────────────┘ └───────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 依赖管理 (dependency-management) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │MyBatis │ │DBCP 1.4 │ │WebJars │ │ │
│ │ │3.5.6 │ │ │ │Bootstrap │ │ │
│ │ │mybatis- │ │ │ │3.4.1 │ │ │
│ │ │spring │ │ │ │jQuery │ │ │
│ │ │1.3.1 │ │ │ │3.6.0 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1.2 CAS 6.6:Gradle 7.5 + Java 11
CAS 6.6 是连接旧时代与新时代的桥梁版本。它保持了 Gradle 7.5 的构建工具版本,但将 Java 运行时从 8 升级到了 11。Java 11 是 LTS(长期支持)版本,带来了模块化系统(Jigsaw)、HTTP Client API、局部变量类型推断(var)等重要特性。
核心构建配置特征:
groovy
// CAS 6.6 典型构建配置(简化版)
plugins {
id 'java'
id 'org.springframework.boot' version '3.0.x'
id 'io.spring.dependency-management' version '1.1.x'
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
dependencies {
// MyBatis 数据层 - 版本未变
implementation 'org.mybatis:mybatis:3.5.6'
implementation 'org.mybatis:mybatis-spring:1.3.1'
// 数据库连接池 - 仍未升级
implementation 'commons-dbcp:commons-dbcp:1.4'
// 前端资源 - 版本小幅升级
implementation 'org.webjars:bootstrap:5.2.3'
implementation 'org.webjars:jquery:3.6.4'
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
CAS 6.6 的 Docker 构建配置:
dockerfile
# CAS 6.6 典型 Dockerfile
FROM eclipse-temurin:11-jdk AS builder
WORKDIR /app
COPY . .
RUN ./gradlew build -x test --no-daemon
FROM eclipse-temurin:11-jre
COPY --from=builder /app/build/libs/*.war /app/cas.war
EXPOSE 8443 8080
ENTRYPOINT ["java", "-jar", "/app/cas.war"]1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
CAS 6.6 的构建体系相比 5.3 有以下变化:
Java 版本升级到 11:这是最大的变化。Java 11 移除了 Java EE 模块(如
javax.xml.bind、javax.annotation等),CAS 需要手动添加这些依赖或迁移到 Jakarta 命名空间。不过 CAS 6.6 仍然基于 Spring Boot 2.x,尚未完全迁移到 Jakarta EE。Docker 基础镜像切换:从通用的 JDK 镜像切换到
eclipse-temurin:11-jdk,这是一个由 Eclipse Adoptium 项目维护的高质量 OpenJDK 发行版。Temurin 通过了 TCK(Technology Compatibility Kit)认证,保证了与 Java 规范的完全兼容。前端资源升级:Bootstrap 从 3.4.1 升级到 5.2.3,这是一个跨越式升级。Bootstrap 5 移除了 jQuery 依赖,但 CAS 仍然保留了 jQuery 用于其他前端功能。
MyBatis 和 DBCP 未升级:值得注意的是,CAS 6.6 保持了 MyBatis 3.5.6 + mybatis-spring 1.3.1 和 commons-dbcp 1.4 的组合。这反映了 CAS 项目在 6.x 时代的保守策略——优先保证稳定性,而非追求依赖的最新版本。
Spring Boot 3.0.x 的引入:CAS 6.6 后期版本开始引入 Spring Boot 3.0.x,这意味着最低 Java 版本要求变成了 17。但 CAS 6.6 的主流配置仍然是 Java 11 + Spring Boot 2.7.x。
CAS 6.6 构建体系的关键痛点:
- JDK 管理仍然手动:开发人员需要手动安装 JDK 11 并设置
JAVA_HOME - 没有 SBOM 生成:不满足日益严格的软件供应链安全合规要求
- 容器镜像构建效率低:每次构建都需要下载全部依赖,缺乏有效的层缓存策略
- 依赖版本管理分散:BOM 和直接版本声明混用,缺乏统一的版本控制策略
1.3 CAS 7.3:Gradle 9.1.0 + Java 21
CAS 7.3 代表了 CAS 构建体系的全面现代化。它不仅升级了 Java 版本,更在构建工具链、依赖管理、容器化、安全合规等多个维度进行了根本性的重构。
核心构建配置:
groovy
// CAS 7.3 核心构建配置(教学简化版)
plugins {
id 'java'
id 'org.springframework.boot' version '3.5.6'
id 'io.spring.dependency-management' version '1.1.7'
// Foojay Toolchains 插件 - 自动 JDK 下载
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.9.0'
// CycloneDX SBOM 插件 - 软件物料清单生成
id 'org.cyclonedx.bom' version '1.10.0'
// Jib 插件 - 多平台容器镜像构建
id 'com.google.cloud.tools.jib' version '3.5.3'
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
vendor = JvmVendorSpec.AMAZON // Amazon Corretto
}
}
dependencies {
// 使用 enforcedPlatform 进行严格版本控制
enforcedPlatform("org.apereo.cas:cas-server-support-bom:${casServerVersion}")
// MyBatis 数据层 - 大幅升级
implementation 'org.mybatis:mybatis:3.5.16'
implementation 'org.mybatis:mybatis-spring:3.0.3'
// 数据库连接池 - 迁移到 DBCP2
implementation 'org.apache.commons:commons-dbcp2:2.10.0'
// 前端资源 - 全面升级
implementation 'org.webjars:bootstrap:5.3.3'
implementation 'org.webjars:jquery:3.7.1'
implementation 'org.webjars.npm:material-components-web:14.0.0'
}
jib {
from {
image = 'azul/zulu-openjdk:21'
}
to {
image = "your-registry/cas:${project.version}"
tags = ['latest', 'jdk21']
}
platforms {
platform {
architecture = 'amd64'
os = 'linux'
}
platform {
architecture = 'arm64'
os = 'linux'
}
}
}
cyclonedxBom {
includeConfigs = ['runtimeClasspath', 'compileClasspath']
schemaVersion = '1.5'
destination = file("build/reports/cyclonedx")
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
CAS 7.3 构建体系架构图:
┌──────────────────────────────────────────────────────────────────────┐
│ CAS 7.3 构建体系 │
├──────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────────┐ ┌────────────────────┐ │
│ │ Gradle 9.1 │──▶│ Foojay │──▶│ Amazon Corretto │ │
│ │ (构建引擎) │ │ Toolchains │ │ JDK 21 (自动下载) │ │
│ │ │ │ (自动JDK管理) │ │ │ │
│ └──────┬───────┘ └──────────────────┘ └────────┬───────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌────────────────────┐ │
│ │ Spring Boot │ │ CAS WAR 包 │ │
│ │ 3.5.6 │ │ (Servlet 6.0) │ │
│ │ │ │ (Jakarta EE 10) │ │
│ └──────┬───────┘ └────────┬───────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 依赖管理 (enforcedPlatform) │ │
│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │
│ │ │MyBatis │ │DBCP2 │ │WebJars │ │Material │ │ │
│ │ │3.5.16 │ │2.10.0 │ │Bootstrap │ │Components │ │ │
│ │ │mybatis- │ │ │ │5.3.3 │ │Web 14.0.0 │ │ │
│ │ │spring │ │ │ │jQuery │ │ │ │ │
│ │ │3.0.3 │ │ │ │3.7.1 │ │ │ │ │
│ │ └───────────┘ └───────────┘ └───────────┘ └───────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Jib 3.5.3 │ │ CycloneDX │ │
│ │ (多平台镜像构建) │ │ (SBOM 生成) │ │
│ │ amd64 + arm64 │ │ schema 1.5 │ │
│ └──────────────────┘ └──────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────┘1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
CAS 7.3 构建体系的核心变革包括:
Toolchains 自动 JDK 管理:通过 Foojay 插件,Gradle 会自动下载并使用指定版本的 JDK,开发人员无需手动安装。这是一个革命性的变化——团队成员只需安装任意版本的 Gradle Wrapper,构建系统会自动处理 JDK 版本问题。
enforcedPlatform 严格版本控制:从
platform升级到enforcedPlatform,这意味着 BOM 中声明的版本具有强制约束力,任何传递依赖都无法覆盖 BOM 中指定的版本。这对于 CAS 这样的大型项目至关重要,可以有效防止依赖版本冲突。CycloneDX SBOM 生成:引入了软件物料清单(Software Bill of Materials)生成能力,满足 Executive Order 14028、EU Cyber Resilience Act 等法规对软件供应链透明度的要求。
Jib 多平台镜像构建:使用 Jib 3.5.3 替代传统的 Dockerfile 构建,支持同时生成 amd64 和 arm64 架构的容器镜像,且无需 Docker daemon。
MyBatis 跨代升级:从 mybatis-spring 1.3.1 升级到 3.0.3,这是一个跨越了 2.x 大版本的重大升级,带来了对 Spring Boot 3.x 的原生自动配置支持。
连接池现代化:从 commons-dbcp 1.4 迁移到 commons-dbcp2 2.10.0,获得了连接池监控、异步获取连接、连接泄漏检测等现代特性。
1.4 构建体系演进对比表
下表汇总了 CAS 三个主要版本在构建体系方面的关键差异:
| 维度 | CAS 5.3 | CAS 6.6 | CAS 7.3 |
|---|---|---|---|
| Gradle 版本 | 7.5 | 7.5 | 9.1.0 |
| Java 版本 | 8 (1.8) | 11 | 21 |
| Java 版本指定方式 | sourceCompatibility / targetCompatibility | sourceCompatibility / targetCompatibility | java.toolchain |
| JDK 管理 | 手动安装 | 手动安装 | Foojay Toolchains 自动下载 |
| JVM Vendor | 任意 | Eclipse Temurin | Amazon Corretto |
| Spring Boot | 2.7.x | 2.7.x / 3.0.x | 3.5.6 |
| Servlet 规范 | Servlet 3.1 | Servlet 4.0 | Servlet 6.0 (Jakarta) |
| 依赖管理 | platform + dependency-management | platform + dependency-management | enforcedPlatform |
| MyBatis | 3.5.6 + mybatis-spring 1.3.1 | 3.5.6 + mybatis-spring 1.3.1 | 3.5.16 + mybatis-spring 3.0.3 |
| 连接池 | commons-dbcp 1.4 | commons-dbcp 1.4 | commons-dbcp2 2.10.0 |
| Bootstrap | 3.4.1 | 5.2.3 | 5.3.3 |
| jQuery | 3.6.0 | 3.6.4 | 3.7.1 |
| Material Components | 无 | 无 | 14.0.0 |
| Docker 基础镜像 | 通用 JDK 8 镜像 | eclipse-temurin:11-jdk | azul/zulu-openjdk:21 |
| 容器构建方式 | Dockerfile | Dockerfile | Jib 3.5.3 |
| 多平台支持 | 无 | 无 | amd64 + arm64 |
| SBOM 生成 | 无 | 无 | CycloneDX 1.5 |
| Configuration Cache | 不支持 | 实验性 | 生产就绪 |
| Groovy/Kotlin DSL | Groovy | Groovy | Groovy(Kotlin 可选) |
版本演进关键里程碑图:
时间线 ─────────────────────────────────────────────────────────────▶
CAS 5.3 CAS 6.6 CAS 7.3
│ │ │
│ Gradle 7.5 │ Gradle 7.5 │ Gradle 9.1.0
│ Java 8 │ Java 11 │ Java 21
│ │ │
│ ───────────────│────────────────────│──────────────
│ │ │
│ │ Spring Boot 3.0 │ Spring Boot 3.5.6
│ │ (部分版本) │ Jakarta EE 10
│ │ │
│ │ │ ───────────────
│ │ │
│ │ │ Foojay Toolchains
│ │ │ CycloneDX SBOM
│ │ │ Jib 多平台
│ │ │ enforcedPlatform
│ │ │ MyBatis 3.0.3
│ │ │ DBCP2 2.10.0
│ │ │
▼ ▼ ▼
┌──────┐ ┌──────┐ ┌──────────┐
│ 2019 │ │ 2022 │ │ 2025 │
└──────┘ └──────┘ └──────────┘1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
第二章 Gradle 9.x 核心特性与 CAS 适配
2.1 Gradle 9.x 的重大变更
Gradle 9.x 是 Gradle 构建工具的一次重大版本升级,它引入了许多破坏性变更(breaking changes),对 CAS 项目的构建脚本产生了深远影响。理解这些变更对于成功迁移到 CAS 7.3 至关重要。
Gradle 版本演进路线:
Gradle 版本演进
═══════════════════════════════════════════════════════════
Gradle 7.x Gradle 8.x Gradle 9.x
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 7.0 │───────▶│ 8.0 │───────▶│ 9.0 │
│ 2021 │ │ 2023 │ │ 2025 │
│ │ │ │ │ │
│ • 配置 │ │ • 配置 │ │ • 配置 │
│ 避免API│ │ 缓存 │ │ 缓存 │
│ • Groovy│ │ 稳定 │ │ 默认 │
│ 3.x │ │ • Kotlin│ │ 启用 │
│ • JVM │ │ DSL │ │ • API │
│ Tool │ │ 增强 │ │ 清理 │
│ Chain │ │ • Isolated│ │ • Groovy│
│ 实验性│ │ Projects│ │ 4.x │
│ │ │ 实验性│ │ • Java │
└─────────┘ └─────────┘ │ 24 基线│
└─────────┘1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Gradle 9.x 的核心变更清单:
Configuration Cache 默认启用:这是对构建脚本影响最大的变更。Configuration Cache 要求所有构建逻辑必须是"无状态"的——不能在配置阶段访问项目状态、不能使用
afterEvaluate回调、不能在配置阶段解析配置文件等。API 清理:大量在 Gradle 7.x/8.x 中被标记为
@Deprecated的 API 被彻底移除。例如,Project.afterEvaluate()的某些用法、旧的DependencyHandler方法签名等。Groovy 4.x 升级:Gradle 9.x 内置的 Groovy 版本从 3.x 升级到 4.x。这意味着构建脚本中使用的 Groovy 语法需要兼容 Groovy 4.x,特别是 GString 插值和闭包委托策略的变化。
Isolated Projects 正式发布:项目隔离机制从实验性功能变为正式功能。每个子项目在配置阶段只能访问自己的项目对象,不能跨项目访问依赖关系。
Java 24 作为基线:Gradle 9.x 本身需要 Java 17+ 才能运行,但推荐使用 Java 21 或更高版本。Gradle 9.x 的 API 设计时以 Java 24 作为目标基线。
CAS 7.3 对 Gradle 9.x 变更的适配策略:
groovy
// CAS 7.3 的 Gradle Wrapper 配置
// gradle/wrapper/gradle-wrapper.properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
CAS 7.3 在适配 Gradle 9.x 时采取了以下策略:
- 保守的插件版本选择:所有插件都选择了与 Gradle 9.x 兼容的版本,避免使用实验性功能
- 构建脚本简化:移除了所有在 Configuration Cache 下不兼容的构建逻辑
- Toolchains 替代手动 JDK 管理:利用 Toolchains API 替代
sourceCompatibility/targetCompatibility
2.2 Configuration Cache 兼容处理
Configuration Cache 是 Gradle 在 8.x 中引入、9.x 中默认启用的性能优化特性。它的核心思想是:将构建的"配置阶段"结果缓存起来,后续构建如果配置没有变化,则直接使用缓存结果,跳过整个配置阶段。
Configuration Cache 工作原理:
传统构建流程(无 Configuration Cache):
═══════════════════════════════════════════════════════════
初始化 ──▶ 配置阶段 ──▶ 执行阶段
│
├── 解析 settings.gradle
├── 解析 build.gradle
├── 评估所有插件
├── 解析依赖声明
├── 配置所有 Task
└── 构建依赖图
[每次构建都要完整执行配置阶段]
启用 Configuration Cache 后的构建流程:
═══════════════════════════════════════════════════════════
首次构建:
初始化 ──▶ 配置阶段 ──▶ 执行阶段 ──▶ 缓存配置结果
│ │
│ └──▶ ~/.gradle/configuration-cache/
│
后续构建(配置未变化):
初始化 ──▶ [读取缓存] ──▶ 执行阶段
│
└── 直接加载缓存的配置结果
[配置阶段被完全跳过,构建速度提升 30-50%]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Configuration Cache 的限制条件:
Configuration Cache 要求构建逻辑满足以下条件:
- 无状态配置:配置阶段的代码不能依赖于外部可变状态
- 无副作用:配置阶段不能执行 I/O 操作(如读取文件、网络请求)
- 无
afterEvaluate:不能使用project.afterEvaluate {}回调 - 无
gradle.projectsLoaded:不能在settings.gradle中使用此回调 - Provider API:所有属性访问必须通过
ProviderAPI 进行懒加载
CAS 7.3 中的 Configuration Cache 兼容处理:
groovy
// ❌ 不兼容 Configuration Cache 的写法(CAS 5.3/6.6 风格)
project.afterEvaluate {
// 在 afterEvaluate 中读取项目属性
def casVersion = project.casServerVersion
println "CAS version: ${casVersion}"
// 动态添加依赖
dependencies {
implementation "org.apereo.cas:cas-server-support-xxx:${casVersion}"
}
}
// ✅ 兼容 Configuration Cache 的写法(CAS 7.3 风格)
// 使用 Provider API 进行懒加载
def casVersion = project.providers.gradleProperty('casServerVersion')
dependencies {
// 使用字符串插值,Gradle 会在执行阶段解析
implementation "org.apereo.cas:cas-server-support-xxx:${casVersion.get()}"
}
// ✅ 使用 lazy configuration
configurations {
integrationTestImplementation {
extendsFrom implementation
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
CAS 7.3 中 Configuration Cache 的配置:
groovy
// CAS 7.3 的 Configuration Cache 配置
// build.gradle
// 显式启用 Configuration Cache(Gradle 9.x 中默认启用)
configurationCache {
// 允许在控制台输出 Configuration Cache 相关的警告
problems {
warnOnProblems()
}
}
// 或者通过命令行参数控制
// ./gradlew build --configuration-cache
// ./gradlew build --no-configuration-cache // 禁用1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
Configuration Cache 迁移常见问题与解决方案:
| 问题 | 原因 | 解决方案 |
|---|---|---|
Cannot call Project.afterEvaluate | afterEvaluate 在 Configuration Cache 下不可用 | 使用 Provider API 或 lazy configuration |
Cannot access Project at configuration time | 配置阶段访问了项目状态 | 将访问延迟到执行阶段 |
Build logic should not read files at configuration time | 配置阶段执行了文件 I/O | 使用 Provider.of() 或 regularFile 属性 |
Provider is not compatible with Configuration Cache | 使用了不兼容的 Provider 实现 | 使用 providers.factory 创建 Provider |
Task properties are not correctly wired | Task 输入输出未正确声明 | 使用 @InputFile、@OutputDirectory 等注解 |
2.3 API 废弃与替代方案
Gradle 9.x 移除了大量在 7.x/8.x 中已废弃的 API。以下是 CAS 构建脚本中常见的影响点:
1. JvmVendorSpec API 变更
groovy
// ❌ Gradle 7.x 风格(在 9.x 中可能不可用)
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
vendor = JvmVendorSpec.AMAZON
}
}
// ✅ Gradle 9.x 兼容写法
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
// 使用 JvmVendorSpec.of() 更安全
vendor = JvmVendorSpec.matching("AMAZON")
}
}
// ✅ 更通用的写法,兼容 Gradle 7.x/8.x/9.x
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
vendor = JvmVendorSpec.AMAZON
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2. 依赖声明 API 变更
groovy
// ❌ 旧式依赖声明(已废弃)
dependencies {
compile group: 'org.mybatis', name: 'mybatis', version: '3.5.16'
runtime group: 'org.apache.commons', name: 'commons-dbcp2', version: '2.10.0'
}
// ✅ 新式依赖声明
dependencies {
implementation 'org.mybatis:mybatis:3.5.16'
runtimeOnly 'org.apache.commons:commons-dbcp2:2.10.0'
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
3. Task 配置 API 变更
groovy
// ❌ 旧式 Task 配置(Configuration Cache 不兼容)
task copyConfig(type: Copy) {
from "src/main/resources"
into "$buildDir/config"
// 在配置阶段解析属性
def env = System.getenv("CAS_ENV") ?: "dev"
rename { filename ->
filename.replace(".template", ".${env}")
}
}
// ✅ 新式 Task 配置(Configuration Cache 兼容)
tasks.register('copyConfig', Copy) {
from layout.projectDirectory.dir("src/main/resources")
into layout.buildDirectory.dir("config")
// 使用 Provider 延迟解析
def env = providers.environmentVariable("CAS_ENV")
.orElse("dev")
rename { filename ->
filename.replace(".template", ".${env.get()}")
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
4. platform 到 enforcedPlatform 的升级
groovy
// CAS 5.3/6.6:使用 platform(推荐但不强制)
dependencies {
platform("org.apereo.cas:cas-server-support-bom:${casServerVersion}")
implementation 'org.apereo.cas:cas-server-support-xxx'
// 如果传递依赖声明了不同版本,可能产生冲突
}
// CAS 7.3:使用 enforcedPlatform(强制版本)
dependencies {
enforcedPlatform("org.apereo.cas:cas-server-support-bom:${casServerVersion}")
implementation 'org.apereo.cas:cas-server-support-xxx'
// BOM 中的版本具有最高优先级,传递依赖无法覆盖
}1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
2.4 Groovy DSL vs Kotlin DSL 选择
CAS 7.3 默认使用 Groovy DSL 编写构建脚本,但 Gradle 9.x 对 Kotlin DSL 的支持已经非常成熟。两种 DSL 各有优劣:
Groovy DSL vs Kotlin DSL 对比:
| 维度 | Groovy DSL | Kotlin DSL |
|---|---|---|
| 学习曲线 | 低(Groovy 语法简单) | 中(需要 Kotlin 基础) |
| 类型安全 | 弱(运行时才发现类型错误) | 强(编译时类型检查) |
| IDE 支持 | 好 | 优秀(自动补全更准确) |
| Configuration Cache | 需要额外注意 | 天然友好 |
| 脚本后缀 | build.gradle | build.gradle.kts |
| CAS 官方支持 | 主要支持 | 可选支持 |
| 社区资源 | 丰富 | 增长中 |
| 构建性能 | 略慢(动态类型) | 略快(静态编译) |
CAS 7.3 选择 Groovy DSL 的原因:
- 向后兼容:CAS 5.3 和 6.6 都使用 Groovy DSL,保持一致性可以降低迁移成本
- 社区资源丰富:Groovy DSL 的教程、示例和社区讨论远多于 Kotlin DSL
- 构建脚本复杂度适中:CAS overlay 项目的构建脚本不需要复杂的类型安全保证
- Gradle 插件兼容性:部分 Gradle 插件对 Groovy DSL 的支持更完善
Kotlin DSL 示例(供参考):
kotlin
// build.gradle.kts - CAS 7.3 的 Kotlin DSL 版本
plugins {
java
id("org.springframework.boot") version "3.5.6"
id("io.spring.dependency-management") version "1.1.7"
id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0"
id("org.cyclonedx.bom") version "1.10.0"
id("com.google.cloud.tools.jib") version "3.5.3"
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
vendor = JvmVendorSpec.AMAZON
}
}
dependencies {
enforcedPlatform("org.apereo.cas:cas-server-support-bom:${casServerVersion}")
implementation("org.mybatis:mybatis:3.5.16")
implementation("org.mybatis:mybatis-spring:3.0.3")
implementation("org.apache.commons:commons-dbcp2:2.10.0")
implementation("org.webjars:bootstrap:5.3.3")
implementation("org.webjars:jquery:3.7.1")
}
jib {
from {
image = "azul/zulu-openjdk:21"
}
to {
image = "your-registry/cas:${project.version}"
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
第三章 Foojay Toolchains 插件深度解析
3.1 Toolchains 概念与价值
Java Toolchains 是 JDK 9 中引入、在 Gradle 6.7 中获得支持的一个重要概念。它将"构建所需的 JDK 版本"与"运行 Gradle 的 JDK 版本"解耦,使得同一个 Gradle 安装可以构建需要不同 JDK 版本的项目。
传统 JDK 管理的痛点:
在没有 Toolchains 的时代,Java 项目的 JDK 管理面临以下问题:
传统 JDK 管理模式
═══════════════════════════════════════════════════════════
开发者 A 的机器:
JAVA_HOME=/usr/lib/jvm/java-8 ← 项目 A 需要
JAVA_HOME=/usr/lib/jvm/java-11 ← 项目 B 需要
JAVA_HOME=/usr/lib/jvm/java-21 ← 项目 C 需要
❌ 只能设置一个 JAVA_HOME
解决方案(传统):
1. 手动切换 JAVA_HOME(容易出错)
2. 使用 SDKMAN(需要额外安装)
3. 使用 jenv(macOS 专用)
4. 在 IDE 中配置(不同 IDE 配置不同)
❌ 团队成员的 JDK 版本不一致
❌ CI/CD 需要安装多个 JDK
❌ 新成员入职配置成本高1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Toolchains 的解决方案:
Toolchains 模式
═══════════════════════════════════════════════════════════
开发者 A 的机器:
Gradle Wrapper(任意版本)
│
▼
Foojay Toolchains 插件
│
├── 检测 build.gradle 中的 toolchain 声明
├── 发现需要 JDK 21 (Amazon Corretto)
├── 检查本地缓存 (~/.gradle/jdks/)
│ ├── 有 → 直接使用
│ └── 没有 → 自动从 Foojay API 下载
│
▼
使用下载的 JDK 21 进行编译和测试
✅ 无需手动安装 JDK
✅ 团队成员自动使用相同版本
✅ CI/CD 无需预装 JDK
✅ 新成员零配置即可构建1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Toolchains 的核心价值:
- 构建可重复性:所有团队成员和 CI/CD 环境使用完全相同的 JDK 版本和发行版
- 降低入门门槛:新成员只需克隆仓库并运行
./gradlew build,无需任何额外配置 - 多版本共存:同一台机器可以同时构建需要不同 JDK 版本的项目
- CI/CD 简化:CI 环境不需要预装特定版本的 JDK,Foojay 插件会自动处理
- 安全性:通过 Foojay API 下载的 JDK 来自经过验证的发行版源
3.2 Foojay 插件配置详解
Foojay Toolchains 插件(org.gradle.toolchains.foojay-resolver-convention)是 Gradle 官方推荐的 Toolchains JDK 下载解决方案。它通过 Foojay Disco API(https://api.foojay.io)自动下载所需的 JDK 发行版。
插件声明:
groovy
// settings.gradle(推荐位置)
plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.9.0'
}
// 或者 build.gradle(也可以)
plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.9.0'
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
为什么推荐在 settings.gradle 中声明?
在 settings.gradle 中声明 Foojay 插件有一个重要优势:它可以在 Gradle 解析 build.gradle 之前就准备好 JDK。这意味着即使 build.gradle 中使用了特定 JDK 版本的 API,Gradle 也能正确处理。
插件声明位置的影响
═══════════════════════════════════════════════════════════
settings.gradle 中声明(推荐):
settings.gradle 解析 → Foojay 插件激活 → JDK 下载 → build.gradle 解析
✅ build.gradle 可以安全使用新版本 JDK 的 API
build.gradle 中声明:
settings.gradle 解析 → build.gradle 解析 → Foojay 插件激活 → JDK 下载
⚠️ build.gradle 解析时可能还没有正确的 JDK1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
Toolchains 声明:
groovy
// build.gradle
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
vendor = JvmVendorSpec.AMAZON
}
}1
2
3
4
5
6
7
2
3
4
5
6
7
Foojay 插件的工作流程:
Foojay Toolchains 工作流程
═══════════════════════════════════════════════════════════
1. Gradle 读取 toolchain 声明
│
├── languageVersion = 21
├── vendor = AMAZON
│
▼
2. Foojay 插件查询本地 JDK 目录
│
├── ~/.gradle/jdks/
├── 操作系统检测 (Linux/macOS/Windows)
├── 架构检测 (x86_64/aarch64)
│
▼
3. 本地查找结果
│
├── 找到匹配的 JDK → 直接使用 ✅
│
└── 未找到匹配的 JDK
│
▼
4. 调用 Foojay Disco API
│
├── GET https://api.foojay.io/disco/v3.0/distributions?
│ package_type=jdk&version=21&distro=amazon_corretto&
│ architecture=x64&operating_system=linux
│
├── 解析响应 JSON
├── 获取下载链接
│
▼
5. 下载并安装 JDK
│
├── 下载到 ~/.gradle/jdks/
├── 解压并验证
├── 注册到 Gradle Toolchain 服务
│
▼
6. 使用 JDK 进行构建
✅1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Foojay 插件的高级配置:
groovy
// settings.gradle - Foojay 插件高级配置
settingsEvaluated {
// 可以通过系统属性控制 Foojay 行为
if (System.getProperty('foojay.disable') == 'true') {
// 禁用自动下载,仅使用本地已安装的 JDK
gradle.settingsEvaluated {
// 不做任何操作
}
}
}
// 通过 gradle.properties 控制行为
// gradle.properties
org.gradle.java.installations.auto-detect=true // 自动检测已安装的 JDK
org.gradle.java.installations.paths=/path/to/jdks // 指定 JDK 搜索路径1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
3.3 JVM Vendor 选择机制
CAS 7.3 选择了 Amazon Corretto 作为 JVM Vendor,这是一个经过深思熟虑的选择。不同的 JDK 发行版在性能、安全更新频率、LTS 支持策略等方面存在差异。
主流 JDK 发行版对比:
| 发行版 | 维护者 | TCK 认证 | LTS 更新 | 特点 | 适用场景 |
|---|---|---|---|---|---|
| Amazon Corretto | Amazon | 是 | 长期 | AWS 优化,免费 | AWS 环境、企业生产 |
| Eclipse Temurin | Eclipse Foundation | 是 | 长期 | 社区驱动,广泛使用 | 通用场景 |
| Azul Zulu | Azul Systems | 是 | 长期 | 性能优化 | 高性能需求 |
| Oracle JDK | Oracle | 是 | 付费 LTS | 官方发行版 | Oracle 生态 |
| OpenJDK | 社区 | 部分 | 社区支持 | 原始发行版 | 开发测试 |
| GraalVM CE | Oracle Labs | 是 | 社区支持 | AOT 编译 | 云原生、微服务 |
CAS 7.3 选择 Amazon Corretto 的原因:
- AWS 生态兼容性:许多 CAS 部署在 AWS 上,Corretto 对 AWS 基础设施有针对性优化
- 免费 LTS 支持:Corretto 提供免费的长期支持更新,无需付费订阅
- 安全补丁及时:Amazon 承诺在关键安全漏洞发布后快速提供补丁
- 生产验证:Corretto 在 Amazon 内部大规模使用,经过了充分的生产验证
- 跨平台一致性:在 Linux、Windows、macOS 上提供一致的体验
JVM Vendor 配置方式:
groovy
// 方式一:使用 JvmVendorSpec 枚举(推荐)
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
vendor = JvmVendorSpec.AMAZON
}
}
// 方式二:使用字符串匹配
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
vendor = JvmVendorSpec.matching("amazon")
}
}
// 方式三:不指定 vendor(让 Gradle 自动选择)
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
// vendor 未指定,Gradle 会选择第一个可用的 JDK 21
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
3.4 JvmVendorSpec 枚举兼容处理
JvmVendorSpec 是 Gradle 提供的 JVM 发行版枚举类,它在不同 Gradle 版本中的 API 存在差异。CAS 7.3 需要处理这种兼容性问题。
Gradle 版本间的 JvmVendorSpec 差异:
Gradle 7.x:
JvmVendorSpec 是枚举类
使用方式:JvmVendorSpec.AMAZON
可用值:ADOPTIUM, AMAZON, APPLE, AZUL, GRAAL_VM,
MICROSOFT, ORACLE, SAP, ...
Gradle 8.x:
JvmVendorSpec 仍然是枚举类
新增:of(String) 工厂方法
使用方式:JvmVendorSpec.AMAZON 或 JvmVendorSpec.of("AMAZON")
Gradle 9.x:
JvmVendorSpec API 进一步优化
推荐使用 matching(String) 方法
使用方式:JvmVendorSpec.matching("AMAZON")1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CAS 7.3 的兼容性处理:
groovy
// CAS 7.3 的兼容性写法 - 同时兼容 Gradle 7.x/8.x/9.x
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
// 方式一:直接使用枚举(最简单,兼容性最好)
vendor = JvmVendorSpec.AMAZON
// 方式二:使用 matching 方法(Gradle 8.3+)
// vendor = JvmVendorSpec.matching("AMAZON")
}
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
为什么 CAS 7.3 选择 JvmVendorSpec.AMAZON 而不是 JvmVendorSpec.of("AMAZON")?
- 向后兼容:
JvmVendorSpec.AMAZON在 Gradle 7.x 中就已经存在,确保了最大程度的兼容性 - 编译时安全:枚举值在编译时就会被验证,拼写错误会被立即发现
- IDE 支持:枚举值可以获得 IDE 的自动补全和文档提示
- 性能:枚举比较比字符串比较更高效
自定义 Vendor 匹配策略:
groovy
// 如果需要更灵活的 vendor 匹配
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
// 匹配所有基于 OpenJDK 的发行版
vendor = JvmVendorSpec.matching(".*")
// 或者匹配特定前缀
// vendor = JvmVendorSpec.matching("AMAZON|ADOPTIUM")
}
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
3.5 多 JDK 版本共存策略
在大型企业中,不同的项目可能需要不同的 JDK 版本。Foojay Toolchains 插件支持多 JDK 版本共存,每个项目可以声明自己需要的 JDK 版本。
多项目多 JDK 场景:
企业多项目 JDK 管理场景
═══════════════════════════════════════════════════════════
~/.gradle/jdks/
├── amazon_corretto-21.0.2/
│ └── (CAS 7.3 使用)
├── eclipse_temurin-17.0.9/
│ └── (其他 Spring Boot 3.x 项目使用)
├── amazon_corretto-11.0.21/
│ └── (遗留项目使用)
└── azul_zulu-8.0.392/
└── (老旧项目使用)
项目 A (CAS 7.3):
toolchain { languageVersion = 21; vendor = AMAZON }
→ 使用 amazon_corretto-21.0.2
项目 B (Spring Boot 3.2):
toolchain { languageVersion = 17; vendor = ADOPTIUM }
→ 使用 eclipse_temurin-17.0.9
项目 C (遗留系统):
toolchain { languageVersion = 11; vendor = AMAZON }
→ 使用 amazon_corretto-11.0.211
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
测试多 JDK 版本兼容性:
CAS 7.3 的构建脚本可以配置为在多个 JDK 版本下运行测试:
groovy
// build.gradle - 多 JDK 版本测试配置
testing {
suites {
// 主测试套件使用 JDK 21
test {
useJUnitPlatform()
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
vendor = JvmVendorSpec.AMAZON
}
}
}
// 集成测试套件使用 JDK 21
integrationTest(JvmTestSuite) {
useJUnitPlatform()
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
vendor = JvmVendorSpec.AMAZON
}
}
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
JDK 磁盘空间管理:
Foojay 下载的 JDK 会存储在 ~/.gradle/jdks/ 目录下。每个 JDK 大约 300-500MB,如果项目多,磁盘占用可能较大。可以通过以下方式管理:
bash
# 查看已下载的 JDK
ls -la ~/.gradle/jdks/
# 清理不再使用的 JDK
rm -rf ~/.gradle/jdks/amazon_corretto-11.0.21/
# 通过 Gradle 命令清理
./gradlew --stop
# 然后手动清理 ~/.gradle/jdks/ 中的旧版本1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
第四章 CycloneDX SBOM 生成
4.1 SBOM 概念与合规要求
SBOM(Software Bill of Materials,软件物料清单)是软件组件的详细清单,类似于食品包装上的成分表。它记录了软件中包含的所有第三方库、框架、工具及其版本信息。
SBOM 的核心价值:
SBOM 在软件供应链安全中的位置
═══════════════════════════════════════════════════════════
软件供应链安全层次模型:
┌─────────────────────────────────────────┐
│ 合规与审计层 │
│ EO 14028 / EU CRA / NIST SP 800-218 │
└────────────────┬────────────────────────┘
│ 要求
▼
┌─────────────────────────────────────────┐
│ SBOM 生成层 │
│ CycloneDX / SPDX / SWID │
└────────────────┬────────────────────────┘
│ 依赖
▼
┌─────────────────────────────────────────┐
│ 构建工具层 │
│ Gradle / Maven / npm / pip │
└────────────────┬────────────────────────┘
│ 分析
▼
┌─────────────────────────────────────────┐
│ 依赖解析层 │
│ 依赖树 / 传递依赖 / 版本冲突 │
└─────────────────────────────────────────┘1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
SBOM 相关法规要求:
| 法规/标准 | 适用范围 | SBOM 要求 | 生效时间 |
|---|---|---|---|
| EO 14028 | 美国联邦政府供应商 | 必须提供 SBOM | 2022-2024 分阶段 |
| EU Cyber Resilience Act | 欧盟市场所有联网产品 | 强制 SBOM | 2027 年全面生效 |
| NIST SP 800-218 | 美国联邦机构 | SSDF 框架包含 SBOM | 已发布 |
| SLSA Framework | 开源项目 | 供应链完整性证明 | 持续演进 |
| Executive Order 14017 | 美国关键基础设施 | 供应链风险评估 | 已签署 |
SBOM 标准对比:
| 标准 | 维护者 | 格式 | 优势 | CAS 选择 |
|---|---|---|---|---|
| CycloneDX | OWASP | JSON/XML | 轻量、专注安全、生态丰富 | ✅ 采用 |
| SPDX | Linux Foundation | JSON/XML/RDF | 许可证分析强 | 未采用 |
| SWID | ISO/IEC | XML | 企业级标识 | 未采用 |
CAS 7.3 选择 CycloneDX 的原因:
- OWASP 背书:CycloneDX 是 OWASP(开放 Web 应用安全项目)的旗舰项目
- Gradle 插件成熟:
org.cyclonedx.bom插件功能完善,与 Gradle 9.x 兼容 - VEX 支持:支持 VEX(Vulnerability Exploitability eXchange),可以标记已知漏洞的处置状态
- 轻量高效:生成的 SBOM 文件体积小,解析速度快
- 工具链完整:有丰富的 SBOM 分析工具(如 Dependency-Track、Grype 等)
4.2 CycloneDX Gradle 插件配置
CAS 7.3 使用 org.cyclonedx.bom 插件生成 SBOM。以下是详细的配置说明。
插件声明:
groovy
// build.gradle
plugins {
id 'org.cyclonedx.bom' version '1.10.0'
}1
2
3
4
2
3
4
完整配置:
groovy
// CAS 7.3 CycloneDX 配置(教学简化版)
cyclonedxBom {
// 包含的配置(即哪些依赖会被纳入 SBOM)
includeConfigs = ['runtimeClasspath', 'compileClasspath']
// 排除的配置
excludeConfigs = ['testRuntimeClasspath', 'testCompileClasspath']
// 排除的 Group ID
excludeGroups = ['org.apereo.cas.test']
// Schema 版本
schemaVersion = '1.5'
// 输出格式
outputFormat = 'json'
// 输出目录
destination = file("build/reports/cyclonedx")
// 输出文件名
outputName = 'cas-sbom'
// 包含项目信息
includeProjectBuildFile = true
// 包含 License 信息
includeLicenseText = false
// 跳过子项目(在多项目构建中)
skipProjects = []
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
配置项详解:
includeConfigs:指定哪些依赖配置会被纳入 SBOM 分析。CAS 7.3 只包含runtimeClasspath和compileClasspath,排除了测试依赖。这是因为 SBOM 主要用于生产环境的安全审计,测试依赖不应纳入。schemaVersion:CycloneDX 规范版本。1.5 是目前最新的稳定版本,支持 VEX(漏洞可利用性交换)、服务组件、VCS 信息等高级特性。outputFormat:输出格式支持json、xml和all。JSON 格式更易于机器处理,XML 格式更易于人工阅读。destination:SBOM 文件的输出目录。CAS 7.3 将其放在build/reports/cyclonedx/下,与测试报告等构建产物放在一起。
运行 SBOM 生成:
bash
# 生成 SBOM
./gradlew cyclonedxBom
# 输出文件
# build/reports/cyclonedx/cas-sbom.json1
2
3
4
5
2
3
4
5
与构建流程集成:
groovy
// 将 SBOM 生成集成到构建流程中
tasks.named('build') {
// build 任务依赖于 cyclonedxBom
dependsOn 'cyclonedxBom'
}
// 或者创建专门的 CI 任务
tasks.register('ciBuild') {
dependsOn 'build'
dependsOn 'cyclonedxBom'
// 可以添加更多 CI 相关的任务
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
4.3 SBOM 报告内容解析
CycloneDX 生成的 SBOM 是一个 JSON 文件,包含了项目的完整依赖信息。以下是 CAS 7.3 SBOM 的典型内容结构。
SBOM 文件结构:
json
{
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
"version": 1,
"metadata": {
"timestamp": "2025-01-15T10:30:00Z",
"tools": [
{
"vendor": "CycloneDX",
"name": "cyclonedx-gradle-plugin",
"version": "1.10.0"
}
],
"component": {
"group": "org.apereo.cas",
"name": "cas-overlay",
"version": "7.3.0",
"type": "application",
"purl": "pkg:maven/org.apereo.cas/cas-overlay@7.3.0",
"properties": [
{
"name": "cdx:gradle:project:version",
"value": "7.3.0"
}
]
}
},
"components": [
{
"group": "org.apereo.cas",
"name": "cas-server-core-api",
"version": "7.3.0",
"type": "library",
"purl": "pkg:maven/org.apereo.cas/cas-server-core-api@7.3.0",
"licenses": [
{
"id": "Apache-2.0"
}
]
},
{
"group": "org.mybatis",
"name": "mybatis",
"version": "3.5.16",
"type": "library",
"purl": "pkg:maven/org.mybatis/mybatis@3.5.16",
"licenses": [
{
"id": "Apache-2.0"
}
]
},
{
"group": "org.apache.commons",
"name": "commons-dbcp2",
"version": "2.10.0",
"type": "library",
"purl": "pkg:maven/org.apache.commons/commons-dbcp2@2.10.0",
"licenses": [
{
"id": "Apache-2.0"
}
]
}
],
"dependencies": [
{
"ref": "pkg:maven/org.apereo.cas/cas-overlay@7.3.0",
"dependsOn": [
"pkg:maven/org.apereo.cas/cas-server-core-api@7.3.0",
"pkg:maven/org.mybatis/mybatis@3.5.16",
"pkg:maven/org.apache.commons/commons-dbcp2@2.10.0"
]
}
]
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
SBOM 关键字段解析:
| 字段 | 说明 | 示例 |
|---|---|---|
bomFormat | BOM 格式标识 | CycloneDX |
specVersion | CycloneDX 规范版本 | 1.5 |
serialNumber | 唯一序列号 | urn:uuid:... |
metadata.timestamp | 生成时间 | ISO 8601 格式 |
metadata.component | 主组件信息 | CAS overlay 项目 |
components[] | 依赖组件列表 | 所有第三方库 |
components[].purl | 包 URL(Package URL) | pkg:maven/... |
components[].licenses | 许可证信息 | Apache-2.0 |
dependencies[] | 依赖关系图 | 组件间的依赖关系 |
Package URL (purl) 格式说明:
purl 格式:pkg:<type>/<namespace>/<name>@<version>
CAS 7.3 中的 purl 示例:
pkg:maven/org.apereo.cas/cas-server-core-api@7.3.0
pkg:maven/org.mybatis/mybatis@3.5.16
pkg:maven/org.apache.commons/commons-dbcp2@2.10.0
pkg:maven/org.webjars/bootstrap@5.3.3
pkg:maven/org.webjars.npm/material-components-web@14.0.01
2
3
4
5
6
7
8
2
3
4
5
6
7
8
4.4 生产环境 SBOM 管理
SBOM 生成只是软件供应链安全的第一步,更重要的是在生产环境中管理和使用 SBOM。
SBOM 生命周期管理:
SBOM 生命周期
═══════════════════════════════════════════════════════════
1. 生成(Generate)
│
├── 构建时自动生成(CycloneDX Gradle 插件)
├── 包含在构建产物中
└── 版本与构建产物绑定
│
▼
2. 存储(Store)
│
├── 与 Docker 镜像一起推送到仓库
├── 上传到 SBOM 管理平台(Dependency-Track)
└── 归档到制品库(Nexus/Artifactory)
│
▼
3. 分析(Analyze)
│
├── 漏洞扫描(Grype/Trivy)
├── 许可证合规检查
└── 依赖风险评估
│
▼
4. 响应(Respond)
│
├── 漏洞修复优先级排序
├── VEX(漏洞可利用性交换)发布
└── 升级依赖版本
│
▼
5. 审计(Audit)
│
├── 定期 SBOM 合规审计
├── 供应链安全报告
└── 监管合规证明1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
SBOM 与 Docker 镜像集成:
dockerfile
# CAS 7.3 Dockerfile - SBOM 集成
FROM azul/zulu-openjdk:21 AS builder
WORKDIR /app
COPY . .
# 生成 SBOM
RUN ./gradlew cyclonedxBom --no-daemon
# 构建应用
RUN ./gradlew build -x test --no-daemon
FROM azul/zulu-openjdk:21
WORKDIR /app
# 复制应用
COPY --from=builder /app/build/libs/*.war /app/cas.war
# 复制 SBOM(重要!)
COPY --from=builder /app/build/reports/cyclonedx/cas-sbom.json /app/sbom.json
# 添加 SBOM 标签(OCI 标准)
LABEL org.opencontainers.image.title="CAS 7.3"
LABEL org.opencontainers.image.version="7.3.0"
LABEL io.cyclonedx.version="1.5"
EXPOSE 8443 8080
ENTRYPOINT ["java", "-jar", "/app/cas.war"]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
SBOM 漏洞扫描集成:
bash
# 使用 Grype 扫描 SBOM 中的漏洞
grype sbom:build/reports/cyclonedx/cas-sbom.json
# 使用 Trivy 扫描
trivy sbom build/reports/cyclonedx/cas-sbom.json
# 使用 Dependency-Track 进行持续监控
# 将 SBOM 上传到 Dependency-Track API
curl -X POST "https://dependency-track.example.com/api/v1/bom" \
-H "X-API-Key: your-api-key" \
-H "Content-Type: multipart/form-data" \
-F "project=CAS-7.3" \
-F "bom=@build/reports/cyclonedx/cas-sbom.json"1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
CI/CD 中的 SBOM 流水线:
yaml
# GitLab CI/CD 示例 - SBOM 集成
stages:
- build
- sbom-scan
- deploy
build:
stage: build
script:
- ./gradlew build cyclonedxBom
artifacts:
paths:
- build/reports/cyclonedx/
- build/libs/
sbom-scan:
stage: sbom-scan
needs: [build]
script:
- grype sbom:build/reports/cyclonedx/cas-sbom.json --fail-on critical
allow_failure: false
deploy:
stage: deploy
needs: [sbom-scan]
script:
- docker build -t cas:7.3 .
- docker push your-registry/cas:7.31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
第五章 Jib 多平台 Docker 镜像构建
5.1 Jib 插件核心概念
Jib 是 Google 开源的 Java 容器化工具,它可以将 Java 应用直接构建为 OCI 兼容的 Docker 镜像,无需 Docker daemon、无需编写 Dockerfile、无需了解 Docker 最佳实践。
传统 Docker 构建的问题:
传统 Docker 构建流程
═══════════════════════════════════════════════════════════
开发者机器 / CI Runner
┌─────────────────────────────────────────┐
│ │
│ 1. 编译 Java 代码 │
│ 2. 运行测试 │
│ 3. 编写 Dockerfile │
│ 4. 安装 Docker daemon │
│ 5. docker build . │
│ ├── 启动 Docker daemon │
│ ├── 复制源代码到 Docker context │
│ ├── 在容器内编译(或复制编译结果) │
│ ├── 安装依赖 │
│ └── 生成镜像层 │
│ 6. docker push │
│ │
│ 问题: │
│ ❌ 需要 Docker daemon(安全风险) │
│ ❌ 需要 Dockerfile(维护成本) │
│ ❌ 需要了解 Docker 最佳实践 │
│ ❌ 多平台构建复杂 │
│ ❌ 层缓存策略需要手动优化 │
│ │
└─────────────────────────────────────────┘1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Jib 的解决方案:
Jib 构建流程
═══════════════════════════════════════════════════════════
开发者机器 / CI Runner
┌─────────────────────────────────────────┐
│ │
│ 1. ./gradlew jib │
│ ├── 分析 Java classpath │
│ ├── 分层打包依赖 │
│ │ ├── 层1: 基础镜像 │
│ │ ├── 层2: 依赖 JAR │
│ │ ├── 层3: 应用资源 │
│ │ └── 层4: 应用 classes │
│ ├── 直接推送到容器仓库 │
│ └── 无需 Docker daemon │
│ │
│ 优势: │
│ ✅ 无需 Docker daemon │
│ ✅ 无需 Dockerfile │
│ ✅ 自动分层和缓存 │
│ ✅ 原生多平台支持 │
│ ✅ 可重现构建 │
│ ✅ 与 Gradle 深度集成 │
│ │
└─────────────────────────────────────────┘1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Jib 的镜像层结构:
Jib 自动生成的镜像层
═══════════════════════════════════════════════════════════
┌─────────────────────────────────┐
│ 层4: Application Classes │ ← 变化最频繁
│ (编译后的 .class 文件) │ 每次代码修改都会变化
├─────────────────────────────────┤
│ 层3: Application Resources │ ← 变化较少
│ (配置文件、静态资源) │ 配置修改时才变化
├─────────────────────────────────┤
│ 层2: Dependencies │ ← 变化最少
│ (第三方 JAR 包) │ 依赖版本变更时才变化
│ (MyBatis, DBCP2, Spring...) │ 可跨项目共享缓存
├─────────────────────────────────┤
│ 层1: Base Image │ ← 几乎不变
│ (azul/zulu-openjdk:21) │ 只在基础镜像更新时变化
└─────────────────────────────────┘
缓存优势:
- 修改代码 → 只重建层4(快速!)
- 修改配置 → 重建层3+层4
- 升级依赖 → 重建层2+层3+层4
- 更换基础镜像 → 全部重建1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
5.2 多平台镜像构建配置
CAS 7.3 使用 Jib 3.5.3 构建多平台 Docker 镜像,同时支持 amd64(x86_64)和 arm64(AArch64)架构。
完整 Jib 配置:
groovy
// CAS 7.3 Jib 配置(教学简化版)
jib {
from {
// 基础镜像
image = 'azul/zulu-openjdk:21'
// 可以指定凭据(如果基础镜像是私有仓库)
// credHelper = 'ecr-login'
}
to {
// 目标镜像仓库
image = "your-registry.example.com/cas:${project.version}"
// 镜像标签
tags = [
"latest",
"jdk21",
project.version.toString()
]
// 可以指定凭据
credHelper = 'ecr-login' // AWS ECR
// 或者使用用户名密码
// auth {
// username = project.findProperty('registryUser')
// password = project.findProperty('registryPassword')
// }
}
// 多平台配置
platforms {
platform {
architecture = 'amd64'
os = 'linux'
}
platform {
architecture = 'arm64'
os = 'linux'
}
}
// 容器配置
container {
// JVM 参数
jvmFlags = [
'-Xms512m',
'-Xmx2048m',
'-XX:+UseG1GC',
'-Dcas.serviceRegistry.watcherEnabled=false',
'-Djava.security.egd=file:/dev/./urandom'
]
// 环境变量
environment = [
'CAS_SERVER_VERSION': project.version.toString(),
'SPRING_PROFILES_ACTIVE': 'production'
]
// 端口
ports = ['8443', '8080']
// 入口点
mainClass = 'org.apereo.cas.web.CasWebApplication'
// 创建时间(UTC)
creationTime = 'USE_CURRENT_TIMESTAMP'
// 用户
user = '1001:1001'
// 工作目录
workingDirectory = '/app'
}
// 构建优化
containerizingMode = 'packaged' // 使用已打包的 WAR 文件
// 输出到本地 Docker daemon(用于测试)
// dockerClient {
// executable = '/usr/bin/docker'
// }
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
多平台构建原理:
Jib 多平台构建流程
═══════════════════════════════════════════════════════════
./gradlew jib
┌──────────────────────────────────────────────────┐
│ Jib 构建引擎 │
│ │
│ 1. 分析 Java classpath(与平台无关) │
│ ├── 解析依赖 JAR │
│ ├── 收集应用 classes │
│ └── 收集应用资源 │
│ │
│ 2. 为每个平台构建镜像层 │
│ ├── amd64/linux │
│ │ ├── 拉取 azul/zulu-openjdk:21 (amd64) │
│ │ ├── 添加依赖层 │
│ │ ├── 添加资源层 │
│ │ └── 添加应用层 │
│ │ │
│ └── arm64/linux │
│ ├── 拉取 azul/zulu-openjdk:21 (arm64) │
│ ├── 添加依赖层 │
│ ├── 添加资源层 │
│ └── 添加应用层 │
│ │
│ 3. 推送到容器仓库 │
│ ├── 推送 amd64 manifest │
│ ├── 推送 arm64 manifest │
│ └── 创建/更新多架构 manifest list │
│ │
└──────────────────────────────────────────────────┘
容器仓库中的存储结构:
your-registry/cas:7.3.0
├── manifest list (多架构索引)
│ ├── amd64 manifest → 指向 amd64 层
│ └── arm64 manifest → 指向 arm64 层
├── amd64 层
│ ├── base layer (azul/zulu-openjdk:21 amd64)
│ ├── dependencies layer
│ ├── resources layer
│ └── application layer
└── arm64 层
├── base layer (azul/zulu-openjdk:21 arm64)
├── dependencies layer
├── resources layer
└── application layer1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
常用 Jib 命令:
bash
# 构建并推送到远程仓库
./gradlew jib
# 构建到本地 Docker daemon
./gradlew jibDockerBuild
# 构建到本地 tar 文件
./gradlew jibBuildTar
# 跳过 Jib 构建
./gradlew build -x jib
# 指定目标镜像
./gradlew jib -Djib.to.image=your-registry/cas:7.3.0
# 指定额外的标签
./gradlew jib -Djib.to.tags=latest,jdk21,production1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
5.3 基础镜像选择策略
CAS 7.3 选择 azul/zulu-openjdk:21 作为基础镜像。基础镜像的选择对容器的安全性、性能和体积都有重要影响。
主流 JDK 21 Docker 镜像对比:
| 镜像 | 维护者 | 大小 (JRE) | TCK 认证 | 特点 |
|---|---|---|---|---|
| azul/zulu-openjdk:21 | Azul Systems | ~200MB | 是 | 性能优化,多架构 |
| eclipse-temurin:21-jre | Eclipse Foundation | ~190MB | 是 | 社区标准 |
| amazoncorretto:21 | Amazon | ~195MB | 是 | AWS 优化 |
| graalvm/jdk:21 | Oracle Labs | ~350MB | 是 | AOT 编译支持 |
| openjdk:21-jdk-slim | Docker Library | ~220MB | 部分 | 官方社区版 |
CAS 7.3 选择 Azul Zulu 的原因:
- 多架构支持完善:Azul Zulu 提供了高质量的 amd64 和 arm64 镜像,与 Jib 的多平台构建完美配合
- 性能优化:Azul 在 JVM 性能优化方面有深厚积累,Zulu JDK 在多项基准测试中表现优异
- TCK 认证:通过完整的 TCK 认证,保证了与 Java 规范的完全兼容
- 安全更新及时:Azul 承诺在关键安全漏洞发布后快速提供更新
- 镜像体积合理:Zulu 的 JRE 镜像体积适中,不会过度膨胀容器
基础镜像版本固定策略:
groovy
// ❌ 不推荐:使用 latest 标签
jib {
from {
image = 'azul/zulu-openjdk:21' // 可能指向不同版本
}
}
// ✅ 推荐:使用具体版本
jib {
from {
image = 'azul/zulu-openjdk-alpine:21.0.2-21.0.2+13'
}
}
// ✅ 更好:使用 digest(完全确定性)
jib {
from {
image = 'azul/zulu-openjdk-alpine@sha256:abc123...'
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
5.4 镜像层优化与缓存
Jib 的分层策略是其核心优势之一。理解层优化原理可以帮助我们最大化构建缓存命中率。
依赖层分离策略:
groovy
// Jib 默认的分层策略
jib {
container {
// 默认分层:
// 层1: 基础镜像
// 层2: 所有依赖 JAR(snapshot 和 release 混在一起)
// 层3: 应用资源
// 层4: 应用 classes
// 可以通过 extraDirectories 添加额外文件
extraDirectories {
paths = [
file('src/main/docker') // Docker 特定配置文件
]
permissions = [
'/app/entrypoint.sh': '755'
]
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
缓存命中率优化技巧:
缓存命中率优化策略
═══════════════════════════════════════════════════════════
场景1:日常开发(只修改 Java 代码)
├── 层1 命中缓存 ✅
├── 层2 命中缓存 ✅
├── 层3 命中缓存 ✅
└── 层4 重建 ⏳(只有这一层需要重建)
→ 构建时间:~10-20 秒
场景2:修改配置文件
├── 层1 命中缓存 ✅
├── 层2 命中缓存 ✅
├── 层3 重建 ⏳
└── 层4 重建 ⏳
→ 构建时间:~20-30 秒
场景3:升级依赖版本
├── 层1 命中缓存 ✅
├── 层2 重建 ⏳
├── 层3 重建 ⏳
└── 层4 重建 ⏳
→ 构建时间:~30-60 秒
场景4:更换基础镜像
├── 层1 重建 ⏳
├── 层2 重建 ⏳
├── 层3 重建 ⏳
└── 层4 重建 ⏳
→ 构建时间:~2-5 分钟1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Jib 与传统 Dockerfile 构建的性能对比:
| 维度 | Jib | Dockerfile |
|---|---|---|
| 首次构建 | ~2-3 分钟 | ~3-5 分钟 |
| 代码修改重建 | ~10-20 秒 | ~30-60 秒 |
| 依赖升级重建 | ~30-60 秒 | ~1-3 分钟 |
| 多平台构建 | 原生支持 | 需要 buildx |
| Docker daemon | 不需要 | 必需 |
| 构建可重现性 | 高(确定性层内容) | 中(依赖构建上下文) |
第六章 Docker 多阶段构建 JDK 21 适配
6.1 基础镜像选择:azul/zulu-openjdk:21
虽然 CAS 7.3 推荐使用 Jib 进行容器化构建,但在某些场景下(如需要自定义 entrypoint、集成非 Java 文件等),传统的 Dockerfile 多阶段构建仍然是必要的。
CAS 7.3 Docker 多阶段构建配置:
dockerfile
# CAS 7.3 Dockerfile(教学简化版)
# ============================================
# 阶段1:构建阶段
# ============================================
FROM azul/zulu-openjdk:21 AS builder
# 安装必要工具
RUN apt-get update && \
apt-get install -y --no-install-recommends \
curl \
unzip \
&& rm -rf /var/lib/apt/lists/*
# 设置工作目录
WORKDIR /app
# 复制 Gradle Wrapper 和配置
COPY gradle/ gradle/
COPY gradlew build.gradle settings.gradle ./
# 使用 Gradle Wrapper 构建(Foojay 会自动下载 JDK 21)
RUN chmod +x gradlew && \
./gradlew dependencies --no-daemon || true
# 复制源代码
COPY src/ src/
# 执行构建
RUN ./gradlew build -x test --no-daemon
# ============================================
# 阶段2:运行时阶段
# ============================================
FROM azul/zulu-openjdk-alpine:21
# 创建非 root 用户
RUN addgroup -S cas && adduser -S cas -G cas
# 设置工作目录
WORKDIR /app
# 复制构建产物
COPY --from=builder /app/build/libs/cas.war /app/cas.war
# 复制 entrypoint 脚本
COPY src/main/docker/entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh
# 复制 SBOM
COPY --from=builder /app/build/reports/cyclonedx/cas-sbom.json /app/sbom.json
# 设置环境变量
ENV JAVA_OPTS="-Xms512m -Xmx2048m -XX:+UseG1GC" \
CAS_SERVER_VERSION="7.3.0" \
TZ=Asia/Shanghai
# 切换到非 root 用户
USER cas
# 暴露端口
EXPOSE 8443 8080
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD curl -f http://localhost:8080/cas/status || exit 1
# 入口点
ENTRYPOINT ["/app/entrypoint.sh"]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
基础镜像选择对比:
JDK 21 Docker 基础镜像选择决策树
═══════════════════════════════════════════════════════════
需要最小镜像体积?
├── 是 → 使用 Alpine 版本
│ ├── azul/zulu-openjdk-alpine:21 (~170MB)
│ └── 注意:Alpine 使用 musl libc,可能有兼容性问题
│
└── 否 → 使用标准版本
├── azul/zulu-openjdk:21 (~220MB) ← CAS 7.3 选择
├── eclipse-temurin:21 (~215MB)
└── amazoncorretto:21 (~210MB)
需要完整 JDK(不只是 JRE)?
├── 是 → 使用 JDK 镜像
│ └── 用于构建阶段或需要 JDK 工具的场景
│
└── 否 → 使用 JRE 镜像(推荐用于生产)
└── 减少攻击面,减小镜像体积1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
6.2 entrypoint.sh JDK 21 兼容修改
CAS 的 entrypoint.sh 脚本是容器启动的关键文件。从 JDK 11 升级到 JDK 21 后,需要对脚本进行多项兼容性修改。
CAS 5.3/6.6 的 entrypoint.sh(旧版):
bash
#!/bin/sh
# CAS 5.3/6.6 entrypoint.sh(简化版)
# 旧版 JVM 参数
JAVA_OPTS="${JAVA_OPTS} -noverify" # ❌ JDK 21 中已废弃
JAVA_OPTS="${JAVA_OPTS} -Xdebug" # ❌ JDK 21 中已废弃
JAVA_OPTS="${JAVA_OPTS} -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:5005"
# 启动 CAS
exec java ${JAVA_OPTS} \
-jar /app/cas.war \
--spring.profiles.active=${SPRING_PROFILES_ACTIVE:-default}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
CAS 7.3 的 entrypoint.sh(新版):
bash
#!/bin/sh
# CAS 7.3 entrypoint.sh(教学简化版)
set -e
# ============================================
# JVM 参数配置
# ============================================
# 内存配置
JAVA_OPTS="${JAVA_OPTS:-} -Xms512m -Xmx2048m"
# GC 配置 - 使用 G1GC(Java 21 默认)
JAVA_OPTS="${JAVA_OPTS} -XX:+UseG1GC"
JAVA_OPTS="${JAVA_OPTS} -XX:MaxGCPauseMillis=200"
JAVA_OPTS="${JAVA_OPTS} -XX:G1HeapRegionSize=8m"
# ✅ 移除 -noverify(JDK 21 中已废弃,默认行为已改变)
# ❌ JAVA_OPTS="${JAVA_OPTS} -noverify"
# ✅ 使用 -agentlib:jdwp 替代 -Xdebug + -Xrunjdwp
if [ "${ENABLE_DEBUG}" = "true" ]; then
JAVA_OPTS="${JAVA_OPTS} -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005"
fi
# ============================================
# CAS 特定配置
# ============================================
# 禁用 WatcherService(容器环境中不需要文件系统监听)
JAVA_OPTS="${JAVA_OPTS} -Dcas.serviceRegistry.watcherEnabled=false"
# 时区设置
JAVA_OPTS="${JAVA_OPTS} -Duser.timezone=${TZ:-Asia/Shanghai}"
# 随机数生成器优化(容器环境中 /dev/random 可能阻塞)
JAVA_OPTS="${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom"
# ============================================
# 健康检查与就绪探针
# ============================================
# 等待数据库就绪(如果配置了数据库)
# wait-for-it.sh ${DB_HOST}:${DB_PORT} --timeout=30 --strict
# ============================================
# 启动 CAS
# ============================================
echo "============================================"
echo "Starting CAS ${CAS_SERVER_VERSION:-7.3.0}"
echo "Java Version: $(java -version 2>&1 | head -1)"
echo "JVM Options: ${JAVA_OPTS}"
echo "Spring Profiles: ${SPRING_PROFILES_ACTIVE:-default}"
echo "============================================"
exec java ${JAVA_OPTS} \
-jar /app/cas.war \
--spring.profiles.active="${SPRING_PROFILES_ACTIVE:-default}" \
--cas.server.prefix="${CAS_SERVER_PREFIX:-https://localhost:8443/cas}" \
--cas.server.name="${CAS_SERVER_NAME:-https://localhost:8443}"1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
entrypoint.sh 关键变更详解:
| 旧参数 | 新参数 | 变更原因 |
|---|---|---|
-noverify | 移除 | JDK 13+ 中 -noverify 已被废弃,JDK 17+ 中默认不进行字节码验证优化 |
-Xdebug | 移除 | JDK 21 中 -Xdebug 已被废弃 |
-Xrunjdwp:... | -agentlib:jdwp:... | JDWP 调试参数格式更新 |
| 无 | -Dcas.serviceRegistry.watcherEnabled=false | 容器环境中禁用文件系统监听 |
| 无 | -Djava.security.egd=file:/dev/./urandom | 优化容器环境中的随机数生成 |
6.3 JVM 参数调整
JDK 21 对 JVM 参数进行了大量优化和调整。CAS 7.3 的 JVM 参数配置需要针对 JDK 21 的特性进行优化。
JDK 21 推荐的 JVM 参数:
bash
# CAS 7.3 生产环境推荐 JVM 参数
JAVA_OPTS="-Xms2g -Xmx2g \ # 堆大小(建议初始和最大相同)
-XX:+UseG1GC \ # 使用 G1 垃圾收集器(JDK 21 默认)
-XX:MaxGCPauseMillis=200 \ # 最大 GC 停顿时间目标
-XX:G1HeapRegionSize=8m \ # G1 区域大小
-XX:+UseStringDeduplication \ # 字符串去重(节省内存)
-XX:+UseCompressedOops \ # 压缩普通对象指针(64位系统)
-XX:+UseCompressedClassPointers \ # 压缩类指针
-XX:+OptimizeStringConcat \ # 字符串连接优化
-Djdk.httpclient.HttpClient.log=errors \ # HTTP Client 日志级别
-Djava.security.egd=file:/dev/./urandom \ # 随机数生成器
-Dcas.serviceRegistry.watcherEnabled=false \ # 禁用 WatcherService
-Dfile.encoding=UTF-8 \ # 文件编码
-Duser.timezone=Asia/Shanghai" # 时区1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
JDK 21 的 GC 选择指南:
JDK 21 垃圾收集器选择决策树
═══════════════════════════════════════════════════════════
你的应用特征是什么?
│
├── 低延迟优先(SSO 认证场景)
│ ├── 堆大小 < 4GB → ZGC(推荐)
│ │ -XX:+UseZGC
│ │ -XX:+ZGenerational # JDK 21 的分代 ZGC
│ │
│ └── 堆大小 >= 4GB → ZGC 或 Shenandoah
│ -XX:+UseZGC -XX:+ZGenerational
│
├── 吞吐量优先(后台处理场景)
│ └── Parallel GC
│ -XX:+UseParallelGC
│
└── 平衡模式(CAS 推荐默认选择)
└── G1GC(JDK 21 默认)
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
CAS 7.3 推荐:G1GC
原因:
1. CAS 是 Web 应用,需要平衡延迟和吞吐量
2. G1GC 是 JDK 21 的默认 GC,经过充分优化
3. G1GC 的可预测停顿时间适合 SSO 认证场景
4. 分代 ZGC 虽然在 JDK 21 中可用,但 G1GC 更成熟1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
JDK 21 新特性对 CAS 的影响:
| JDK 21 特性 | 对 CAS 的影响 | 配置建议 |
|---|---|---|
| Virtual Threads (JEP 444) | Spring Boot 3.5.6 可以利用虚拟线程提升并发性能 | -Djdk.virtualThreadScheduler.parallelism=200 |
| Generational ZGC (JEP 439) | 低延迟场景的备选 GC | -XX:+UseZGC -XX:+ZGenerational |
| Record Patterns (JEP 440) | CAS 内部代码可以使用模式匹配简化逻辑 | 无需 JVM 参数 |
| String Templates (JEP 430) | 预览特性,暂不建议在生产环境使用 | -XX:+EnablePreview(不推荐) |
| Sequenced Collections (JEP 431) | CAS 内部代码可以使用新的集合接口 | 无需 JVM 参数 |
6.4 WatcherService 禁用与服务注册
CAS 使用 WatcherService 来监听文件系统变化,实现服务注册表的动态更新。但在容器环境中,这种机制不仅没有必要,还可能导致问题。
WatcherService 的问题:
WatcherService 在容器环境中的问题
═══════════════════════════════════════════════════════════
容器环境特点:
├── 文件系统是只读层(overlay2)
├── 配置文件通过环境变量或 ConfigMap 注入
├── 不需要监听文件系统变化
└── WatcherService 可能消耗不必要的资源
WatcherService 的潜在问题:
├── 1. inotify watch 限制
│ Linux 默认限制每个用户最多 8192 个 inotify watch
│ CAS 的 WatcherService 可能消耗大量 watch
│ → 导致 "too many open files" 错误
│
├── 2. 性能影响
│ WatcherService 会创建后台线程轮询文件变化
│ 在高负载场景下可能影响性能
│
└── 3. 安全风险
WatcherService 需要文件系统访问权限
在安全加固的容器中可能被拒绝
解决方案:
-Dcas.serviceRegistry.watcherEnabled=false
✅ 禁用 WatcherService
✅ 减少资源消耗
✅ 避免潜在的文件系统问题1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
CAS 服务注册在容器环境中的替代方案:
yaml
# application.yml - CAS 7.3 容器环境服务注册配置
cas:
service-registry:
# 禁用 WatcherService
watcher-enabled: false
# 使用 JSON 文件服务注册(推荐用于容器环境)
json:
location: file:/app/services/*.json
# 或者使用数据库服务注册
# jdbc:
# driver-class: org.postgresql.Driver
# url: jdbc:postgresql://db:5432/cas_services
# user: cas
# password: ${DB_PASSWORD}
# ddl-auto: create-drop
# 定时刷新服务注册表(替代 WatcherService)
reload-interval: PT5M # 每 5 分钟刷新一次1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
完整的容器环境 JVM 参数配置:
bash
# CAS 7.3 容器环境完整 JVM 参数
JAVA_OPTS="\
# 内存配置
-Xms2g -Xmx2g \
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m \
-XX:ReservedCodeCacheSize=128m \
# GC 配置
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:G1HeapRegionSize=8m \
-XX:+UseStringDeduplication \
# 容器环境适配
-Dcas.serviceRegistry.watcherEnabled=false \
-Djava.security.egd=file:/dev/./urandom \
-Dfile.encoding=UTF-8 \
-Duser.timezone=Asia/Shanghai \
# 日志配置
-Dlogging.config=file:/app/config/logback.xml \
-Dlogback.configurationFile=file:/app/config/logback.xml \
# 网络配置
-Dserver.port=8443 \
-Dserver.ssl.enabled=true \
-Dcas.server.prefix=https://cas.example.com/cas \
-Dcas.server.name=https://cas.example.com \
# 健康检查
-Dmanagement.endpoints.web.exposure.include=health,info,metrics \
-Dmanagement.endpoint.health.show-details=always"1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
第七章 依赖管理与版本控制
7.1 enforcedPlatform 严格版本控制
CAS 7.3 从 platform 升级到 enforcedPlatform 进行依赖版本控制,这是一个重要的策略变化。
platform vs enforcedPlatform 对比:
platform vs enforcedPlatform 行为差异
═══════════════════════════════════════════════════════════
场景:BOM 声明 commons-dbcp2 版本为 2.10.0
但某个传递依赖声明 commons-dbcp2 版本为 2.9.0
┌─────────────────────────────────────────┐
│ platform(Gradle 推荐模式) │
│ │
│ BOM 版本: 2.10.0 │
│ 传递依赖: 2.9.0 │
│ 结果: 2.9.0(传递依赖优先) │
│ ⚠️ 可能使用不兼容的版本 │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ enforcedPlatform(CAS 7.3 选择) │
│ │
│ BOM 版本: 2.10.0 │
│ 传递依赖: 2.9.0 │
│ 结果: 2.10.0(BOM 强制覆盖) │
│ ✅ 确保使用经过验证的版本 │
└─────────────────────────────────────────┘1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
CAS 7.3 的 enforcedPlatform 配置:
groovy
// CAS 7.3 依赖管理配置(教学简化版)
ext {
casServerVersion = '7.3.0'
}
dependencies {
// 使用 enforcedPlatform 进行严格版本控制
enforcedPlatform("org.apereo.cas:cas-server-support-bom:${casServerVersion}")
// CAS 核心依赖
implementation "org.apereo.cas:cas-server-support-rest:${casServerVersion}"
implementation "org.apereo.cas:cas-server-support-oauth:${casServerVersion}"
implementation "org.apereo.cas:cas-server-support-oidc:${casServerVersion}"
implementation "org.apereo.cas:cas-server-support-mybatis:${casServerVersion}"
// 数据库相关
implementation 'org.mybatis:mybatis:3.5.16'
implementation 'org.mybatis:mybatis-spring:3.0.3'
implementation 'org.apache.commons:commons-dbcp2:2.10.0'
// 前端资源
implementation 'org.webjars:bootstrap:5.3.3'
implementation 'org.webjars:jquery:3.7.1'
implementation 'org.webjars.npm:material-components-web:14.0.0'
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
enforcedPlatform 的注意事项:
版本冲突处理:当
enforcedPlatform与直接声明的版本冲突时,Gradle 会报错。这实际上是好事——它强制开发者显式处理版本冲突。与 Spring Boot BOM 的配合:CAS 7.3 同时使用了 CAS BOM 和 Spring Boot BOM,需要注意两者的优先级。
groovy
// CAS 7.3 多 BOM 配置
dependencies {
// Spring Boot BOM(优先级较低)
platform("org.springframework.boot:spring-boot-dependencies:3.5.6")
// CAS BOM(优先级较高,使用 enforcedPlatform)
enforcedPlatform("org.apereo.cas:cas-server-support-bom:${casServerVersion}")
// 当两者都声明了同一个依赖的版本时:
// enforcedPlatform 的版本会胜出
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
- 排除特定依赖:如果需要覆盖 BOM 中的某个版本,可以使用
exclude:
groovy
dependencies {
enforcedPlatform("org.apereo.cas:cas-server-support-bom:${casServerVersion}")
// 排除 BOM 中的某个依赖版本
implementation("org.apereo.cas:cas-server-support-xxx:${casServerVersion}") {
exclude group: 'com.example', module: 'unwanted-library'
}
// 显式指定版本(覆盖 BOM)
implementation 'com.example:specific-library:2.0.0'
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
7.2 MyBatis 跨版本升级:1.3.1 到 3.0.3
MyBatis Spring 集成模块从 1.3.1 升级到 3.0.3 是 CAS 7.3 依赖升级中最具挑战性的部分。这是一个跨越了 2.x 大版本的重大升级。
MyBatis 版本演进路线:
MyBatis Spring 集成模块版本演进
═══════════════════════════════════════════════════════════
mybatis-spring 1.x(CAS 5.3/6.6 使用)
┌─────────────────────────────────────────┐
│ mybatis-spring 1.3.1 │
│ ├── 支持 Spring 3.x / 4.x / 5.x │
│ ├── 手动配置 SqlSessionFactory │
│ ├── 手动配置 MapperScannerConfigurer │
│ ├── 不支持 Spring Boot 自动配置 │
│ └── Java 7+ │
└─────────────────────────────────────────┘
│
▼
mybatis-spring 2.x(过渡版本)
┌─────────────────────────────────────────┐
│ mybatis-spring 2.x │
│ ├── 支持 Spring 5.x / 6.x │
│ ├── 引入 Spring Boot 自动配置 │
│ ├── 支持 Jakarta Persistence API │
│ └── Java 8+ │
└─────────────────────────────────────────┘
│
▼
mybatis-spring 3.x(CAS 7.3 使用)
┌─────────────────────────────────────────┐
│ mybatis-spring 3.0.3 │
│ ├── 支持 Spring 6.x(Spring Boot 3.x) │
│ ├── 完整的 Spring Boot 自动配置 │
│ ├── 使用 Jakarta EE 命名空间 │
│ ├── 支持 Spring Boot 3.x 配置属性 │
│ └── Java 17+ │
└─────────────────────────────────────────┘1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
关键变更详解:
- 命名空间变更(javax → jakarta)
java
// ❌ mybatis-spring 1.3.1(CAS 5.3/6.6)
import javax.sql.DataSource;
import javax.transaction.Transactional;
// ✅ mybatis-spring 3.0.3(CAS 7.3)
import jakarta.sql.DataSource;
import jakarta.transaction.Transactional;1
2
3
4
5
6
7
2
3
4
5
6
7
- 配置方式变更
java
// ❌ mybatis-spring 1.3.1 手动配置方式
@Configuration
public class MyBatisConfig {
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setTypeAliasesPackage("org.apereo.cas");
bean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath:mapper/*.xml"));
return bean.getObject();
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
MapperScannerConfigurer configurer = new MapperScannerConfigurer();
configurer.setBasePackage("org.apereo.cas.mapper");
configurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
return configurer;
}
}
// ✅ mybatis-spring 3.0.3 Spring Boot 自动配置
// application.yml
mybatis:
type-aliases-package: org.apereo.cas
mapper-locations: classpath:mapper/*.xml
configuration:
map-underscore-to-camel-case: true
cache-enabled: true
lazy-loading-enabled: false
default-executor-type: REUSE1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
- Mapper 接口变更
java
// ❌ mybatis-spring 1.3.1 风格
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface UserMapper {
@Select("SELECT * FROM users WHERE username = #{username}")
User findByUsername(String username);
}
// ✅ mybatis-spring 3.0.3 风格(接口不变,但注解处理方式更新)
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Options;
@Mapper
public interface UserMapper {
@Select("SELECT * FROM users WHERE username = #{username}")
@Options(useCache = true, timeout = 10000)
User findByUsername(String username);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
MyBatis 3.5.16 的新特性:
| 特性 | 版本 | 说明 |
|---|---|---|
| 支持 Java 21 | 3.5.16 | 完全兼容 Java 21 |
| 支持 Jakarta EE | 3.5.14+ | 使用 Jakarta 命名空间 |
| 性能优化 | 3.5.x | SQL 解析和参数处理优化 |
| 类型处理器增强 | 3.5.x | 支持更多 Java 类型的自动映射 |
| 缓存改进 | 3.5.x | 二级缓存性能优化 |
| 日志改进 | 3.5.x | 支持 SLF4J 2.x |
7.3 commons-dbcp 到 commons-dbcp2 迁移
从 commons-dbcp 1.4 迁移到 commons-dbcp2 2.10.0 是 CAS 7.3 依赖升级的另一个重要部分。
DBCP 版本对比:
| 特性 | commons-dbcp 1.4 | commons-dbcp2 2.10.0 |
|---|---|---|
| Java 版本 | Java 6+ | Java 8+ |
| 连接池实现 | BasicDataSource | BasicDataSource |
| 异步获取连接 | 不支持 | 支持(maxTotal + maxIdle) |
| 连接泄漏检测 | 不支持 | removeAbandonedOnBorrow |
| 连接验证 | validationQuery | validationQuery + 自动验证 |
| JMX 监控 | 有限 | 完整的 JMX 支持 |
| 连接池预热 | 不支持 | initialSize |
| 线程安全 | synchronized | 并发优化 |
| 包名 | org.apache.commons.dbcp | org.apache.commons.dbcp2 |
迁移配置对比:
java
// ❌ commons-dbcp 1.4 配置(CAS 5.3/6.6)
import org.apache.commons.dbcp.BasicDataSource;
@Bean
public DataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/cas");
ds.setUsername("cas");
ds.setPassword("password");
ds.setMaxActive(50); // DBCP2 中改为 setMaxTotal()
ds.setMaxIdle(30);
ds.setMinIdle(10);
ds.setInitialSize(10);
ds.setMaxWait(10000); // DBCP2 中改为 setMaxWaitMillis()
ds.setValidationQuery("SELECT 1");
ds.setTestOnBorrow(true);
ds.setTestWhileIdle(true);
ds.setTimeBetweenEvictionRunsMillis(30000);
ds.setMinEvictableIdleTimeMillis(60000);
return ds;
}
// ✅ commons-dbcp2 2.10.0 配置(CAS 7.3)
import org.apache.commons.dbcp2.BasicDataSource;
@Bean
public DataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("com.mysql.cj.jdbc.Driver"); // MySQL Connector/J 8.x
ds.setUrl("jdbc:mysql://localhost:3306/cas?useSSL=false&serverTimezone=UTC");
ds.setUsername("cas");
ds.setPassword("password");
ds.setMaxTotal(50); // 原 maxActive
ds.setMaxIdle(30);
ds.setMinIdle(10);
ds.setInitialSize(10);
ds.setMaxWaitMillis(10000); // 原 maxWait
ds.setValidationQuery("SELECT 1");
ds.setTestOnBorrow(true);
ds.setTestWhileIdle(true);
ds.setTimeBetweenEvictionRunsMillis(30000);
ds.setMinEvictableIdleTimeMillis(60000);
// DBCP2 新增特性
ds.setRemoveAbandonedOnBorrow(true); // 连接泄漏检测
ds.setRemoveAbandonedTimeout(300); // 300 秒未关闭视为泄漏
ds.setLogAbandoned(true); // 记录泄漏连接的堆栈
ds.setPoolPreparedStatements(true); // 预编译语句缓存
ds.setMaxOpenPreparedStatements(50); // 最大缓存语句数
return ds;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
Spring Boot 自动配置方式(推荐):
yaml
# application.yml - CAS 7.3 数据源配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:cas}?useSSL=false&serverTimezone=UTC
username: ${DB_USER:cas}
password: ${DB_PASSWORD:password}
type: org.apache.commons.dbcp2.BasicDataSource
dbcp2:
max-total: 50
max-idle: 30
min-idle: 10
initial-size: 10
max-wait-millis: 10000
validation-query: SELECT 1
test-on-borrow: true
test-while-idle: true
time-between-eviction-runs-millis: 30000
min-evictable-idle-time-millis: 60000
remove-abandoned-on-borrow: true
remove-abandoned-timeout: 300
log-abandoned: true
pool-prepared-statements: true
max-open-prepared-statements: 501
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
DBCP2 连接池监控配置:
java
// 启用 JMX 监控
@Bean
public DataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
// ... 其他配置 ...
// 启用 JMX
ds.setJmxName("org.apache.commons.dbcp2:type=DataSource,name=CAS");
return ds;
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
7.4 WebJars 前端依赖管理
CAS 7.3 的前端依赖管理经历了重大变化,引入了 Material Design Components,并升级了 Bootstrap 和 jQuery。
WebJars 版本升级对比:
| 依赖 | CAS 5.3 | CAS 6.6 | CAS 7.3 | 变化说明 |
|---|---|---|---|---|
| Bootstrap | 3.4.1 | 5.2.3 | 5.3.3 | 跨代升级(3→5) |
| jQuery | 3.6.0 | 3.6.4 | 3.7.1 | 小版本升级 |
| Material Components | 无 | 无 | 14.0.0 | 新引入 |
| Popper.js | 无 | 2.11.6 | 2.11.8 | Bootstrap 5 依赖 |
CAS 7.3 WebJars 配置:
groovy
// CAS 7.3 前端依赖配置
dependencies {
// Bootstrap 5.3.3 - 完全重写的 CSS 框架
implementation 'org.webjars:bootstrap:5.3.3'
// jQuery 3.7.1 - 虽然 Bootstrap 5 不再依赖 jQuery,但 CAS 仍需要
implementation 'org.webjars:jquery:3.7.1'
// Material Components Web 14.0.0 - Google Material Design 组件
implementation 'org.webjars.npm:material-components-web:14.0.0'
// Popper.js - Bootstrap 5 的依赖
implementation 'org.webjars:popper.js:2.11.8'
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
Bootstrap 3 到 5 的迁移要点:
Bootstrap 3 → 5 迁移关键变化
═══════════════════════════════════════════════════════════
CSS 类名变更:
├── .panel → .card
├── .well → .card-body
├── .thumbnail → .card + .img-fluid
├── .label → .badge
├── .form-group → .mb-3
├── .input-group-addon → .input-group-text
├── .hidden-xs/sm/md/lg → .d-none .d-sm-none ...
├── .pull-left/right → .float-start/end
├── .text-left/right/center → .text-start/end/center
└── .col-xs-* → .col-*(无 xs 断点)
JavaScript 变更:
├── 移除 jQuery 依赖(Bootstrap 5 原生 JS)
├── data-* 属性命名变更(camelCase → kebab-case)
├── 事件命名变更(如 shown.bs.modal 不变)
└── 工具提示和弹出框需要 Popper.js
CAS 的影响:
├── 登录页面 UI 需要重新设计
├── 自定义主题 CSS 需要全面更新
├── JavaScript 插件调用方式需要调整
└── 响应式断点需要重新测试1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Material Components Web 集成:
CAS 7.3 引入 Material Components Web (MDC-Web) 14.0.0,为 CAS 的管理界面提供 Material Design 风格的 UI 组件。
groovy
// Material Components Web 依赖
implementation 'org.webjars.npm:material-components-web:14.0.0'1
2
2
html
<!-- CAS 7.3 中使用 Material Components -->
<!DOCTYPE html>
<html>
<head>
<!-- Material Icons -->
<link rel="stylesheet" href="/webjars/material-components-web/14.0.0/material-components-web.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
</head>
<body>
<!-- Material Button -->
<button class="mdc-button mdc-button--raised">
<span class="mdc-button__ripple"></span>
<span class="mdc-button__label">Login</span>
</button>
<!-- Material Text Field -->
<label class="mdc-text-field mdc-text-field--outlined">
<span class="mdc-notched-outline">
<span class="mdc-notched-outline__leading"></span>
<span class="mdc-notched-outline__notch">
<span class="mdc-floating-label" id="my-label-id">Username</span>
</span>
<span class="mdc-notched-outline__trailing"></span>
</span>
<input type="text" class="mdc-text-field__input" aria-labelledby="my-label-id">
</label>
<script src="/webjars/material-components-web/14.0.0/material-components-web.min.js"></script>
<script>
// 初始化 Material Components
mdc.textField.MDCTextField.attachTo(document.querySelector('.mdc-text-field'));
mdc.ripple.MDCRipple.attachTo(document.querySelector('.mdc-button'));
</script>
</body>
</html>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
WebJars 版本管理最佳实践:
groovy
// 集中管理 WebJars 版本
ext {
webjarsVersions = [
bootstrap: '5.3.3',
jquery: '3.7.1',
materialComponentsWeb: '14.0.0',
popperJs: '2.11.8'
]
}
dependencies {
implementation "org.webjars:bootstrap:${webjarsVersions.bootstrap}"
implementation "org.webjars:jquery:${webjarsVersions.jquery}"
implementation "org.webjars.npm:material-components-web:${webjarsVersions.materialComponentsWeb}"
implementation "org.webjars:popper.js:${webjarsVersions.popperJs}"
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
总结与展望
升级路径总结
本文从七个维度系统性地分析了 CAS 从 5.3/6.6 升级到 7.3 的构建体系变革。以下是核心升级路径的总结:
CAS 构建体系升级全景图
═══════════════════════════════════════════════════════════
┌──────────────────────────────────────────────────────────┐
│ CAS 7.3 升级全景 │
│ │
│ 构建工具层 │
│ ├── Gradle 7.5 → 9.1.0 │
│ ├── sourceCompatibility → java.toolchain │
│ ├── 手动 JDK → Foojay Toolchains 自动下载 │
│ └── Configuration Cache 默认启用 │
│ │
│ 依赖管理层 │
│ ├── platform → enforcedPlatform │
│ ├── mybatis-spring 1.3.1 → 3.0.3 │
│ ├── commons-dbcp 1.4 → commons-dbcp2 2.10.0 │
│ └── Bootstrap 3.4.1 → 5.3.3 + MDC 14.0.0 │
│ │
│ 容器化层 │
│ ├── Dockerfile → Jib 3.5.3 │
│ ├── 单平台 → 多平台 (amd64 + arm64) │
│ ├── eclipse-temurin:11 → azul/zulu-openjdk:21 │
│ └── entrypoint.sh JDK 21 兼容修改 │
│ │
│ 安全合规层 │
│ ├── 无 SBOM → CycloneDX 1.5 │
│ ├── 无漏洞扫描 → SBOM + Grype/Trivy │
│ └── 无供应链透明度 → 完整依赖追踪 │
│ │
│ 运行时层 │
│ ├── Java 8/11 → Java 21 │
│ ├── Spring Boot 2.7 → 3.5.6 │
│ ├── javax → jakarta │
│ └── -noverify/-Xdebug → -agentlib:jdwp │
│ │
└──────────────────────────────────────────────────────────┘1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
升级建议
对于计划将 CAS 从 6.x 升级到 7.3 的团队,我们建议以下升级路径:
第一阶段:构建工具升级
- 升级 Gradle Wrapper 到 9.1.0
- 引入 Foojay Toolchains 插件
- 将
sourceCompatibility迁移到java.toolchain - 修复 Configuration Cache 兼容性问题
第二阶段:依赖升级
- 将
platform升级到enforcedPlatform - 升级 MyBatis 到 3.5.16 + mybatis-spring 3.0.3
- 迁移 commons-dbcp 到 commons-dbcp2
- 更新前端 WebJars 版本
- 将
第三阶段:容器化升级
- 引入 Jib 插件替代 Dockerfile
- 配置多平台镜像构建
- 更新 entrypoint.sh 兼容 JDK 21
- 调整 JVM 参数
第四阶段:安全合规
- 引入 CycloneDX SBOM 生成
- 集成漏洞扫描工具
- 建立 SBOM 管理流程
第五阶段:运行时适配
- 处理 javax → jakarta 命名空间迁移
- 更新自定义认证处理器
- 测试所有自定义扩展的兼容性
未来展望
CAS 构建体系的演进仍在继续。以下几个方向值得关注:
- Gradle Kotlin DSL:随着 Kotlin DSL 的成熟,CAS 未来可能会提供官方的 Kotlin DSL 构建脚本
- GraalVM Native Image:CAS 已经开始探索 GraalVM 原生镜像编译,这将大幅缩短启动时间
- SBOM 自动化:SBOM 生成将更加自动化,与 CI/CD 流水线深度集成
- AI 辅助构建:AI 工具可能被用于自动检测依赖冲突、推荐版本升级等
- 云原生构建:构建过程将进一步向云端迁移,利用远程构建缓存和分布式构建
版权声明: 本文为必码(bima.cc)原创技术文章,仅供学习交流。
本文内容基于实际项目源码解析整理,代码示例均为教学简化版本,仅供学习参考。
如需获取完整项目代码或技术支持,请访问 bima.cc。