Appearance
CAS 监控与日志体系深度配置:从 Log4j2 异步日志到 Actuator 端点管控
作者: 必码 | bima.cc
前言
在企业级单点登录(SSO)系统中,Apereo CAS(Central Authentication Service)作为业界最成熟的开源认证方案之一,承载着整个组织的身份认证核心链路。一个生产环境的 CAS 集群每天可能处理数百万次认证请求,每一次请求背后都涉及票据创建、验证、销毁等关键操作。在这样的高并发场景下,日志体系和监控体系的配置质量,直接决定了系统的可观测性(Observability)、故障排查效率以及安全合规能力。
本文基于我们团队在实际 CAS Overlay 项目中积累的生产环境部署经验,从 CAS 5.3.x、6.6.x 到 7.3.x 三个主要版本线出发,系统性地解析 CAS 的日志架构设计、Log4j2 异步日志的深度配置、审计日志与性能统计日志的运作机制、Spring Boot Actuator 端点的安全管控策略,以及日志收集与分析的完整链路。所有配置示例均来自实际项目的简化教学版本,力求让读者能够直接应用于生产环境。
读者对象: 具备 CAS 基础部署经验、需要深入了解监控与日志体系配置的 Java 技术架构师和运维工程师。
一、CAS 日志架构概述
1.1 三日志分离设计
CAS 从架构层面采用了三日志分离的设计理念,将不同关注点的日志输出到独立的日志文件中。这种设计在日志分析、存储策略、安全合规等方面具有显著优势。以下是三种核心日志文件及其职责划分:
| 日志文件 | 职责定位 | 典型内容 | 保留策略建议 |
|---|---|---|---|
cas.log | 应用运行日志 | 框架启动、Bean 加载、认证流程、异常堆栈 | 30天滚动 |
cas_audit.log | 审计追踪日志 | 认证成功/失败、票据生命周期、服务访问记录 | 180天+(合规要求) |
perfStats.log | 性能统计日志 | 票据处理耗时、认证处理器响应时间 | 90天滚动 |
为什么需要三日志分离?
首先,从存储成本角度考虑,应用日志(cas.log)在排查问题时需要保留较长时间,但审计日志(cas_audit.log)可能需要满足等保2.0、GDPR 等合规要求,保留周期通常长达半年到一年。性能统计日志(perfStats.log)则主要用于容量规划和性能基线建立,保留周期介于两者之间。如果将所有日志混在一个文件中,要么全部长期保留导致存储浪费,要么统一清理导致合规风险。
其次,从查询效率角度考虑,分离后的日志文件体积更小,grep、awk 等文本处理工具的搜索速度更快。在 ELK Stack 等日志平台中,不同日志可以映射到不同的 Index Pattern,实现更精准的检索和可视化。
最后,从安全管控角度考虑,审计日志通常需要更严格的访问控制(仅安全管理员可读),而应用日志和性能日志的开发团队访问权限可以相对宽松。分离存储便于实施差异化的文件系统权限策略。
1.2 Log4j2 作为 CAS 默认日志框架
CAS 从 5.x 系列开始,将 Log4j2 作为默认的日志框架。这一选择基于以下技术考量:
性能优势: Log4j2 的 LMAX Disruptor 异步日志机制,在多线程高并发场景下的吞吐量远超 Logback 和 Log4j 1.x。根据官方基准测试数据,Log4j2 异步模式下的日志吞吐量可达 Logback 的 12 倍以上,延迟降低至微秒级别。
零垃圾回收(Zero GC): Log4j2 的异步 Logger 使用 ring buffer 和数组结构传递日志事件,避免了传统异步日志框架中频繁的对象创建和垃圾回收压力。对于 CAS 这种长时间运行的服务端应用,这一特性尤为重要。
配置灵活性: Log4j2 支持 XML、JSON、YAML 三种配置格式,并且支持运行时配置热更新(通过 monitorInterval 参数)。CAS 5.3/6.6 使用 XML 格式配置,7.3 则迁移到 Spring Boot 原生的 YAML 日志配置方式。
SLF4J 桥接能力: CAS 内部大量组件使用 SLF4J 作为日志门面,Log4j2 通过 log4j-slf4j-impl 模块实现了与 SLF4J 的无缝桥接。在我们的实际项目中,CAS 5.3 的 build.gradle 和 pom.xml 中都明确排除了 log4j-slf4j-impl 的传递依赖,以避免与 CAS 自带的日志桥接模块产生冲突:
groovy
// CAS 5.3 Gradle 构建配置中的日志依赖排除
implementation('org.apereo.cas:cas-server-core') {
exclude group: 'org.apache.logging.log4j', module: 'log4j-slf4j-impl'
exclude group: 'org.springframework.boot', module: 'spring-boot-devtools'
}1
2
3
4
5
2
3
4
5
xml
<!-- CAS 5.3 Maven 构建配置中的日志依赖排除 -->
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-core</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
</exclusion>
</exclusions>
</dependency>1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
同时,在 Gradle 的全局配置中,我们还需要排除 Logback 的干扰:
groovy
// CAS 6.6/7.3 Gradle 全局依赖排除
configurations {
all {
exclude(group: "ch.qos.logback", module: "logback-core")
exclude(group: "ch.qos.logback", module: "logback-classic")
exclude(group: "org.apache.logging.log4j", module: "log4j-to-slf4j")
}
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
1.3 日志配置文件的版本演进
CAS 的日志配置方式经历了显著的版本演进,反映了 Spring Boot 生态的变化趋势:
CAS 5.3.x / 6.6.x -- Log4j2 XML 配置:
这两个版本使用传统的 log4j2.xml 文件进行日志配置。配置文件位于 src/main/resources/etc/logs/ 目录下(开发环境)或 /etc/cas/config/ 目录下(生产环境)。XML 格式提供了最完整的 Log4j2 特性支持,包括自定义 Appender、复杂的滚动策略、异步日志配置等。
xml
<!-- CAS 5.3/6.6 log4j2.xml 配置文件头 -->
<?xml version="1.0" encoding="UTF-8"?>
<Configuration monitorInterval="5" packages="org.apereo.cas.logging">
<Properties>
<Property name="baseDir">src/main/resources/etc/logs</Property>
</Properties>
...
</Configuration>1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
CAS 7.3.x -- Spring Boot YAML 日志配置:
CAS 7.3 基于 Spring Boot 3.x,全面拥抱 Spring Boot 原生的日志配置方式,不再使用独立的 log4j2.xml 文件,而是在 application.yml 中通过 logging.* 命名空间进行配置:
yaml
# CAS 7.3 application.yml 日志配置
logging:
level:
org.apereo.cas: INFO
cc.bima.cas: INFO
org.springframework: WARN
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
file:
name: src/main/resources/etc/logs/cas.log
max-size: 100MB
max-history: 301
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
版本迁移的关键差异点:
| 特性 | CAS 5.3/6.6 (XML) | CAS 7.3 (YAML) |
|---|---|---|
| 配置文件 | log4j2.xml | application.yml |
| 异步日志 | AsyncLogger 显式配置 | Spring Boot 自动配置 |
| 自定义 Appender | CasAppender 支持 | 需要编程式扩展 |
| 审计日志 | 独立 RollingFile | 合并到主日志或独立配置 |
| 性能日志 | perfStatsLogger 独立配置 | 需要自定义实现 |
| 热更新 | monitorInterval="5" | 需要重启或使用 Spring Cloud Config |
| Logback 排除 | 手动排除依赖 | Spring Boot Starter 自动管理 |
1.4 CasAppender 自定义日志 Appender
CAS 框架在 Log4j2 的标准 Appender 之上封装了自定义的 CasAppender,这是理解 CAS 日志架构的关键组件。CasAppender 的类全限定名为 org.apereo.cas.logging.CasAppender,它在 log4j2.xml 的 <Configuration> 标签中通过 packages="org.apereo.cas.logging" 属性被引入。
CasAppender 的核心设计思想是代理模式(Proxy Pattern):它本身不直接处理日志输出,而是将日志事件委托给内部引用的标准 Log4j2 Appender。这种设计为 CAS 提供了在日志输出前后插入自定义逻辑的扩展点。
在我们的实际项目中,log4j2.xml 定义了四个 CasAppender 实例:
xml
<!-- CasAppender 代理配置 -->
<CasAppender name="casAudit">
<AppenderRef ref="auditlogfile" />
</CasAppender>
<CasAppender name="casFile">
<AppenderRef ref="file" />
</CasAppender>
<CasAppender name="casConsole">
<AppenderRef ref="console" />
</CasAppender>
<CasAppender name="casPerf">
<AppenderRef ref="perfFileAppender" />
</CasAppender>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
这四个 CasAppender 分别代理了审计日志文件、应用日志文件、控制台输出和性能统计日志文件。在 <Loggers> 配置中,所有的 <AsyncLogger> 都通过 <AppenderRef ref="casXxx" /> 引用 CasAppender,而非直接引用底层 Appender。
CasAppender 的实际价值:
- 日志上下文增强: CasAppender 可以在日志事件中自动注入 CAS 特有的上下文信息,如请求 ID、会话 ID、认证流水号等。
- 日志脱敏拦截: 在日志输出前对敏感信息(如密码、Token)进行自动脱敏处理。
- 日志路由控制: 根据运行环境(开发/测试/生产)动态调整日志输出策略。
- 审计日志增强: 为审计日志添加额外的安全元数据,如客户端 IP、地理位置、设备指纹等。
二、Log4j2 配置深度解析(5.3/6.6)
2.1 log4j2.xml 完整结构
CAS 5.3 和 6.6 的 log4j2.xml 配置文件遵循 Log4j2 的标准 XML Schema,整体结构分为四个核心层级:<Configuration>(根配置)、<Properties>(属性定义)、<Appenders>(输出器定义)、<Loggers>(日志器定义)。
以下是我们实际项目中 log4j2.xml 的完整结构解析:
xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- 根配置节点 -->
<Configuration monitorInterval="5" packages="org.apereo.cas.logging">
<!-- 第一层:全局属性定义 -->
<Properties>
<Property name="baseDir">src/main/resources/etc/logs</Property>
</Properties>
<!-- 第二层:Appender 定义 -->
<Appenders>
<!-- 控制台 Appender -->
<!-- 应用日志 RollingFile Appender -->
<!-- 审计日志 RollingFile Appender -->
<!-- 性能统计 RollingFile Appender -->
<!-- CasAppender 代理层 -->
</Appenders>
<!-- 第三层:Logger 定义 -->
<Loggers>
<!-- 各包的 AsyncLogger 配置 -->
<!-- 根 Logger 配置 -->
</Loggers>
</Configuration>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
<Configuration> 根节点属性解析:
monitorInterval="5":配置文件热更新间隔为 5 秒。Log4j2 会在后台线程中定期检查配置文件是否发生变化,如果检测到修改则自动重新加载配置。这在生产环境中非常有用,可以在不重启 CAS 服务的情况下动态调整日志级别。packages="org.apereo.cas.logging":声明自定义插件包,使 Log4j2 能够识别CasAppender等自定义组件。这个包路径对应 CAS 源码中的org.apereo.cas.logging包,其中包含了 CAS 框架提供的所有自定义日志扩展。
2.2 AsyncLogger 异步日志配置
异步日志是 Log4j2 的核心性能优势所在。CAS 的 log4j2.xml 中,几乎所有的 Logger 都使用了 <AsyncLogger> 而非同步的 <Logger>。这种配置方式利用了 LMAX Disruptor 高性能环形缓冲区,将日志写入操作从业务线程中完全解耦。
异步日志的工作原理:
业务线程 → Log Event → Ring Buffer → 异步线程 → Appender → 文件/控制台1
业务线程仅需将日志事件放入 Ring Buffer 即可返回,实际的 I/O 写入操作由独立的后台线程完成。这种设计将日志写入的延迟从毫秒级降低到纳秒级。
CAS 中的 AsyncLogger 配置示例:
xml
<!-- CAS 核心框架日志 -->
<AsyncLogger name="org.apereo.cas.web.CasWebApplication" level="info"
additivity="false" includeLocation="true">
<AppenderRef ref="casConsole" />
<AppenderRef ref="casFile" />
</AsyncLogger>
<!-- 审计日志 -->
<AsyncLogger name="org.apereo.inspektr.audit.support" level="info"
includeLocation="true">
<AppenderRef ref="casAudit" />
<AppenderRef ref="casFile" />
</AsyncLogger>
<!-- 性能统计日志 -->
<AsyncLogger name="perfStatsLogger" level="info" additivity="false"
includeLocation="true">
<AppenderRef ref="casPerf" />
</AsyncLogger>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
关键属性解析:
additivity="false":禁止日志事件向父 Logger 传播。设置为 false 可以避免同一条日志被多个 Logger 重复输出。在我们的配置中,大部分 AsyncLogger 都设置了additivity="false",但org.apereo.cas.web.flow是一个例外(additivity="true"),因为 Web Flow 的日志需要同时输出到文件和根 Logger。includeLocation="true":在日志事件中包含调用者的类名、方法名和行号信息。这个选项会带来轻微的性能开销(因为需要获取调用栈),但对于调试和问题排查至关重要。在生产环境中,如果性能敏感,可以考虑关闭此选项。level="info":设置日志级别。CAS 的核心组件通常使用info级别,而 Spring 框架的大部分组件则被设置为off(完全关闭)或error(仅输出错误)。
异步日志的性能调优参数:
虽然 CAS 默认的 log4j2.xml 中没有显式配置 Disruptor 的参数,但在高并发生产环境中,可以通过系统属性进行调优:
bash
# 设置 Ring Buffer 大小(默认 256*1024)
-DAsyncLogger.RingBufferSize=524288
# 设置等待策略(默认 Timeout)
-DAsyncLogger.WaitStrategy=Yield
# 设置异步日志线程数(默认 CPU 核心数 + 1)
-DAsyncLogger.ThreadNumFactor=2.01
2
3
4
5
6
7
8
2
3
4
5
6
7
8
等待策略的选择:
| 等待策略 | CPU 占用 | 延迟 | 适用场景 |
|---|---|---|---|
Sleep | 最低 | 最高 | 低吞吐量、低延迟要求 |
Yield | 中等 | 中等 | 通用场景(推荐) |
BusySpin | 最高 | 最低 | 超高吞吐量、超低延迟 |
2.3 RollingFileAppender 滚动策略
CAS 的三个日志文件都使用了 RollingFileAppender,配合多种触发策略和清理策略,实现了自动化的日志轮转和空间管理。
应用日志 RollingFile 配置:
xml
<RollingFile name="file" fileName="${baseDir}/cas.log" append="true"
filePattern="${baseDir}/cas-%d{yyyy-MM-dd-HH}-%i.log">
<PatternLayout pattern="%highlight{%d %p [%c] - <%m>}%n" />
<Policies>
<OnStartupTriggeringPolicy />
<SizeBasedTriggeringPolicy size="10 MB" />
<TimeBasedTriggeringPolicy />
</Policies>
<DefaultRolloverStrategy max="5" compressionLevel="9">
<Delete basePath="${baseDir}" maxDepth="2">
<IfFileName glob="*/*.log.gz" />
<IfLastModified age="7d" />
</Delete>
</DefaultRolloverStrategy>
</RollingFile>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
逐行深度解析:
fileName="${baseDir}/cas.log":当前活跃日志文件的路径。${baseDir}引用了<Properties>中定义的baseDir属性,值为src/main/resources/etc/logs。在生产环境中,建议修改为/var/log/cas或/data/logs/cas等标准日志目录。append="true":以追加模式写入日志文件。每次 CAS 重启后不会清空已有日志,这对于故障排查和日志连续性至关重要。filePattern="${baseDir}/cas-%d{yyyy-MM-dd-HH}-%i.log":归档日志文件的命名模式。%d{yyyy-MM-dd-HH}表示按小时进行时间维度分割,%i表示同小时内如果文件大小超限,使用递增序号区分。<Policies>触发策略组合:<OnStartupTriggeringPolicy />:CAS 服务启动时触发一次日志轮转。这确保了每次启动都有独立的日志文件,便于追踪每次启动后的行为。<SizeBasedTriggeringPolicy size="10 MB" />:当当前日志文件达到 10MB 时触发轮转。这个值在开发环境中是合理的,但在生产高并发环境中,建议调整为 100MB-500MB,以减少日志文件碎片。<TimeBasedTriggeringPolicy />:按时间周期触发轮转。配合filePattern中的%d{yyyy-MM-dd-HH},实现每小时自动轮转。
<DefaultRolloverStrategy max="5">:同时间窗口内最多保留 5 个归档文件(即%i的最大值为 5)。超过 5 个后,最早的归档文件将被删除。compressionLevel="9":使用 GZIP 压缩归档日志文件,压缩级别为 9(最高压缩率)。虽然压缩过程会消耗少量 CPU,但可以将日志文件体积压缩到原来的 10%-20%,显著节省磁盘空间。<Delete>清理策略:xml<Delete basePath="${baseDir}" maxDepth="2"> <IfFileName glob="*/*.log.gz" /> <IfLastModified age="7d" /> </Delete>1
2
3
4maxDepth="2":搜索深度为 2 层目录。<IfFileName glob="*/*.log.gz" />:仅匹配已压缩的归档日志文件。<IfLastModified age="7d" />:删除 7 天前的归档文件。这意味着日志文件的最长保留周期为 7 天。
审计日志和性能日志的 RollingFile 配置与应用日志结构相同,但有以下差异:
- 审计日志(
auditlogfile)的 PatternLayout 不使用%highlight高亮,因为审计日志通常由机器解析,不需要颜色标记。 - 性能日志(
perfFileAppender)的 PatternLayout 仅使用%m%n,即只输出消息内容本身,不包含时间戳和日志级别。这是因为性能统计日志通常由 CAS 内部的性能记录器自行格式化时间信息。
2.4 日志级别控制
CAS 的 log4j2.xml 中定义了大量的日志级别控制规则,覆盖了 CAS 内部组件、Spring 框架、第三方库等各个层面。这种精细化的级别控制是生产环境日志管理的关键。
日志级别优先级(从低到高):
ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF1
CAS 核心组件日志级别:
xml
<!-- CAS 主应用启动日志 -->
<AsyncLogger name="org.apereo.cas.web.CasWebApplication" level="info" />
<!-- CAS 框架通用日志 -->
<AsyncLogger name="org.apereo" level="info" />
<!-- CAS Web Flow 日志 -->
<AsyncLogger name="org.apereo.cas.web.flow" level="info" />
<!-- CAS 审计日志 -->
<AsyncLogger name="org.apereo.inspektr.audit.support" level="info" />
<!-- CAS 性能统计日志 -->
<AsyncLogger name="perfStatsLogger" level="info" />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
Spring 框架日志级别(大量设置为 off):
xml
<!-- Spring Security 核心日志 -->
<AsyncLogger name="org.springframework.security" level="off" />
<!-- Spring Boot 自动配置日志 -->
<AsyncLogger name="org.springframework.boot.autoconfigure.security" level="info" />
<AsyncLogger name="org.springframework.boot.actuate.autoconfigure" level="off" />
<!-- Spring 通用日志 -->
<AsyncLogger name="org.springframework" level="off" />
<AsyncLogger name="org.springframework.web" level="off" />
<AsyncLogger name="org.springframework.webflow" level="off" />
<AsyncLogger name="org.springframework.session" level="off" />
<AsyncLogger name="org.springframework.aop" level="off" />
<AsyncLogger name="org.springframework.orm.jpa" level="off" />
<AsyncLogger name="org.springframework.scheduling" level="off" />
<!-- Spring Cloud 日志 -->
<AsyncLogger name="org.springframework.cloud" level="info" />
<AsyncLogger name="org.springframework.cloud.context" level="off" />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
第三方库日志级别:
xml
<!-- Apache 基础库 -->
<AsyncLogger name="org.apache" level="error" />
<AsyncLogger name="org.apache.cxf" level="off" />
<AsyncLogger name="org.apache.http" level="off" />
<!-- 认证相关库 -->
<AsyncLogger name="org.pac4j" level="off" />
<AsyncLogger name="org.opensaml" level="off" />
<AsyncLogger name="org.ldaptive" level="off" />
<!-- 缓存和消息 -->
<AsyncLogger name="net.sf.ehcache" level="off" />
<AsyncLogger name="com.hazelcast" level="off" />
<AsyncLogger name="com.couchbase" level="off" />
<!-- 模板引擎 -->
<AsyncLogger name="org.thymeleaf" level="off" />1
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
根 Logger 配置:
xml
<AsyncRoot level="error">
<AppenderRef ref="casConsole" />
</AsyncRoot>1
2
3
2
3
根 Logger 的级别设置为 error,这意味着所有未被显式配置的 Logger 都只会输出 ERROR 级别及以上的日志。这是一个非常保守但安全的默认策略,可以有效控制日志量。
日志级别调优建议:
- 开发环境: 将
org.apereo和org.springframework的级别调整为debug,便于排查问题。 - 测试环境: 保持
info级别,但开启org.apereo.cas.web.flow的debug级别。 - 生产环境: 严格按照上述配置,仅在排查特定问题时临时调整相关包的日志级别(利用
monitorInterval="5"的热更新能力)。
2.5 日志格式 Pattern Layout
Pattern Layout 是 Log4j2 中控制日志输出格式的核心组件。CAS 的三个日志文件使用了不同的 Pattern Layout,体现了不同日志文件的用途差异。
控制台 Pattern Layout:
xml
<PatternLayout pattern="%d %p [%c] - <%m>%n" />1
输出示例:
2024-01-15 10:30:45,678 INFO [org.apereo.cas.web.CasWebApplication] - <CAS Server started successfully>1
应用日志文件 Pattern Layout(带高亮):
xml
<PatternLayout pattern="%highlight{%d %p [%c] - <%m>}%n" />1
%highlight{} 是 Log4j2 提供的颜色高亮包装器,会根据日志级别自动添加 ANSI 颜色代码:
- ERROR/FATAL:红色
- WARN:黄色
- INFO:绿色
- DEBUG/TRACE:蓝色
注意:高亮仅在支持 ANSI 颜色的终端中有效,在日志文件中会显示为 ESC 转义序列。因此,高亮仅应用于控制台 Appender,不应用于文件 Appender(除非确定需要彩色日志文件)。
审计日志 Pattern Layout:
xml
<PatternLayout pattern="%d %p [%c] - %m%n" />1
审计日志不使用 %highlight 和 < > 包裹,保持纯文本格式,便于日志收集工具解析和审计系统对接。
性能统计日志 Pattern Layout:
xml
<PatternLayout pattern="%m%n" />1
性能日志仅输出消息本身,因为 CAS 的性能记录器(perfStatsLogger)内部已经格式化了完整的统计信息,包括时间戳、操作类型、耗时等。
Pattern Layout 转换字符参考:
| 转换符 | 描述 | 示例输出 |
|---|---|---|
%d | 日期时间 | 2024-01-15 10:30:45,678 |
%p | 日志级别 | INFO |
%c | Logger 名称 | org.apereo.cas.web.CasWebApplication |
%m | 日志消息 | Authentication successful for user admin |
%n | 行分隔符 | 系统相关 |
%t | 线程名称 | http-nio-8443-exec-1 |
%highlight{} | ANSI 颜色高亮 | 彩色输出 |
%throwable{} | 异常堆栈 | 完整堆栈信息 |
%X{key} | MDC 值 | 请求上下文信息 |
2.6 环境变量引用
Log4j2 支持在配置文件中引用环境变量和系统属性,这使得同一份配置文件可以在不同环境中灵活使用。
基础属性引用:
xml
<Properties>
<Property name="baseDir">src/main/resources/etc/logs</Property>
</Properties>1
2
3
2
3
<Property> 标签定义了可以在整个配置文件中通过 ${baseDir} 引用的变量。这是一种简单的变量替换机制。
环境变量引用方式:
Log4j2 支持多种环境变量引用方式:
xml
<!-- 方式一:直接引用环境变量 -->
<Property name="logDir">${env:LOG_DIR:-/var/log/cas}</Property>
<!-- 方式二:引用系统属性 -->
<Property name="logDir">${sys:log.dir:-/var/log/cas}</Property>
<!-- 方式三:引用 Spring Boot 配置属性(需特殊处理) -->
<Property name="casHome">${spring.application.home:-/etc/cas}</Property>1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
生产环境推荐配置:
xml
<Properties>
<!-- 通过环境变量控制日志目录,提供默认值 -->
<Property name="baseDir">${env:CAS_LOG_DIR:-/var/log/cas}</Property>
<!-- 通过环境变量控制日志级别 -->
<Property name="casLogLevel">${env:CAS_LOG_LEVEL:-info}</Property>
</Properties>1
2
3
4
5
6
2
3
4
5
6
通过环境变量控制日志配置,可以在 Docker/Kubernetes 部署中实现配置与镜像的分离:
bash
# Docker 启动时注入日志目录
docker run -e CAS_LOG_DIR=/data/logs/cas -e CAS_LOG_LEVEL=debug cas-server:latest
# Kubernetes ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: cas-config
data:
CAS_LOG_DIR: "/var/log/cas"
CAS_LOG_LEVEL: "info"1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
2.7 日志文件路径配置
日志文件路径的配置需要考虑开发环境和生产环境的差异。在我们的实际项目中,开发环境使用相对路径,生产环境使用绝对路径。
开发环境路径(CAS 5.3/6.6):
xml
<Property name="baseDir">src/main/resources/etc/logs</Property>1
这个相对路径指向项目源码目录下的 etc/logs 文件夹,便于开发者在 IDE 中直接查看日志文件。
生产环境路径建议:
xml
<Property name="baseDir">${env:CAS_LOG_DIR:-/var/log/cas}</Property>1
Linux 标准日志目录结构:
/var/log/cas/
|-- cas.log # 当前活跃应用日志
|-- cas-2024-01-15-10-0.log.gz # 归档应用日志
|-- cas_audit.log # 当前活跃审计日志
|-- cas_audit-2024-01-15-10-0.log.gz # 归档审计日志
|-- perfStats.log # 当前活跃性能日志
|-- perfStats-2024-01-15-10-0.log.gz # 归档性能日志1
2
3
4
5
6
7
2
3
4
5
6
7
Docker 环境中的路径映射:
yaml
# docker-compose.yml
services:
cas-server:
image: cas-server:latest
volumes:
- /data/logs/cas:/var/log/cas
environment:
- CAS_LOG_DIR=/var/log/cas1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
文件系统权限建议:
bash
# 创建日志目录并设置权限
sudo mkdir -p /var/log/cas
sudo chown cas:cas /var/log/cas
sudo chmod 750 /var/log/cas
# 审计日志目录设置更严格的权限
sudo mkdir -p /var/log/cas/audit
sudo chown cas:audit-admin /var/log/cas/audit
sudo chmod 640 /var/log/cas/audit1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
三、YAML 日志配置(7.3)
3.1 logging.level 配置
CAS 7.3 基于 Spring Boot 3.x,日志配置方式发生了根本性变化。不再使用独立的 log4j2.xml 文件,而是在 application.yml 中通过 logging.level 命名空间进行配置。
CAS 7.3 实际项目配置:
yaml
# ====================== 日志配置 ======================
logging:
level:
org.apereo.cas: INFO
cc.bima.cas: INFO
org.springframework: WARN
org.springframework.web: WARN
org.springframework.security: WARN1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
与 CAS 5.3/6.6 的对比分析:
CAS 5.3/6.6 的 log4j2.xml 中定义了超过 30 个包级别的日志控制规则,而 CAS 7.3 的 YAML 配置仅保留了 5 条核心规则。这种简化反映了以下变化:
- Spring Boot 自动日志管理: Spring Boot 3.x 内置了智能的日志级别默认值,大部分第三方库的日志已经被合理地控制,不需要手动逐一配置。
- Log4j2 自动配置: Spring Boot 的
spring-boot-starter-log4j2自动集成了 Log4j2,并通过log4j2-spring.xml或系统属性提供配置入口。 - 配置精简原则: CAS 7.3 鼓励使用最小化配置,仅覆盖必要的日志级别。
包级别日志控制详解:
yaml
logging:
level:
# CAS 核心框架 -- INFO 级别,输出认证流程关键信息
org.apereo.cas: INFO
# 项目自定义包 -- INFO 级别,输出业务逻辑日志
cc.bima.cas: INFO
# Spring 框架核心 -- WARN 级别,仅输出警告和错误
org.springframework: WARN
# Spring Web 层 -- WARN 级别,过滤 HTTP 请求细节日志
org.springframework.web: WARN
# Spring Security -- WARN 级别,过滤安全框架内部日志
org.springframework.security: WARN1
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 7.3 中,可以通过 Actuator 端点动态调整日志级别(需要先暴露 loggers 端点):
bash
# 查看当前日志级别
curl -u admin:password https://cas.example.com:8443/cas/status/loggers
# 动态调整日志级别
curl -u admin:password -X POST \
-H "Content-Type: application/json" \
-d '{"configuredLevel": "DEBUG"}' \
https://cas.example.com:8443/cas/status/loggers/org.apereo.cas1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
3.2 logging.pattern.console
CAS 7.3 通过 logging.pattern.console 定义控制台日志输出格式:
yaml
logging:
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"1
2
3
2
3
格式解析:
| 转换符 | 描述 | 示例输出 |
|---|---|---|
%d{yyyy-MM-dd HH:mm:ss} | 精确到秒的时间戳 | 2024-01-15 10:30:45 |
[%thread] | 线程名(方括号包裹) | [http-nio-8443-exec-1] |
%-5level | 左对齐的 5 字符日志级别 | INFO |
%logger{36} | 缩短到 36 字符的 Logger 名 | o.a.c.web.CasWebApplication |
%msg | 日志消息 | Authentication successful |
%n | 换行符 | - |
输出示例:
2024-01-15 10:30:45 [http-nio-8443-exec-1] INFO o.a.c.web.CasWebApplication - CAS Server initialized successfully
2024-01-15 10:30:46 [http-nio-8443-exec-2] WARN o.s.web.servlet.PageNotFound - No mapping for GET /favicon.ico1
2
2
与 CAS 5.3/6.6 的格式差异:
CAS 5.3/6.6 的控制台格式为 %d %p [%c] - <m>%n,而 CAS 7.3 增加了线程名和更精确的时间格式。线程名在高并发问题排查中非常重要,可以帮助识别请求处理线程和潜在的资源竞争问题。
3.3 logging.file 配置
CAS 7.3 使用 Spring Boot 的标准日志文件配置:
yaml
logging:
file:
name: src/main/resources/etc/logs/cas.log
max-size: 100MB
max-history: 301
2
3
4
5
2
3
4
5
配置项解析:
name:日志文件路径。CAS 7.3 默认只有一个主日志文件,不再自动分离审计日志和性能日志。如果需要分离,需要通过 Log4j2 的log4j2-spring.xml进行自定义配置。max-size:单个日志文件的最大大小(100MB)。当文件达到此大小时,会自动触发轮转。max-history:归档日志文件的最大保留天数(30天)。超过 30 天的归档文件会被自动删除。
生产环境建议配置:
yaml
logging:
file:
name: ${CAS_LOG_DIR:/var/log/cas}/cas.log
max-size: 500MB
max-history: 90
logback:
rollingpolicy:
total-size-cap: 50GB
clean-history-on-start: true1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
3.4 从 XML 到 YAML 的迁移
对于从 CAS 5.3/6.6 升级到 7.3 的项目,日志配置的迁移是重要的变更项。以下是迁移的关键步骤和注意事项。
迁移对照表:
| CAS 5.3/6.6 (XML) | CAS 7.3 (YAML) | 说明 |
|---|---|---|
<Property name="baseDir"> | logging.file.name | 日志目录 |
<RollingFile> | logging.file.* | 滚动文件配置 |
<AsyncLogger level="info"> | logging.level.org.apereo.cas: INFO | 日志级别 |
<PatternLayout> | logging.pattern.console | 日志格式 |
monitorInterval="5" | Spring Cloud Config | 热更新 |
CasAppender | 自定义 Log4j2 配置 | CAS 扩展 |
perfStatsLogger | 自定义实现 | 性能日志 |
<Delete> | logging.file.max-history | 清理策略 |
迁移步骤:
- 移除 log4j2.xml 文件: CAS 7.3 不再使用独立的 log4j2.xml 文件。
- 在 application.yml 中添加 logging 配置: 按照上述格式添加基本的日志配置。
- 如需保留三日志分离: 创建
src/main/resources/log4j2-spring.xml文件,Spring Boot 会自动识别并加载此文件。 - 如需保留 CasAppender: 在
log4j2-spring.xml中配置packages="org.apereo.cas.logging"。 - 验证日志输出: 启动 CAS 服务,检查日志是否按预期输出。
log4j2-spring.xml 示例(CAS 7.3 兼容):
如果需要在 CAS 7.3 中保留完整的三日志分离能力,可以创建以下文件:
xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration monitorInterval="30" packages="org.apereo.cas.logging">
<Properties>
<Property name="baseDir">${env:CAS_LOG_DIR:-/var/log/cas}</Property>
</Properties>
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n" />
</Console>
<RollingFile name="file" fileName="${baseDir}/cas.log"
filePattern="${baseDir}/cas-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="%d %p [%c] - %m%n" />
<Policies>
<SizeBasedTriggeringPolicy size="100MB" />
<TimeBasedTriggeringPolicy />
</Policies>
<DefaultRolloverStrategy max="30">
<Delete basePath="${baseDir}" maxDepth="1">
<IfFileName glob="cas-*.log.gz" />
<IfLastModified age="30d" />
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
<!-- 审计日志和性能日志 Appender 配置类似 -->
</Appenders>
<Loggers>
<AsyncLogger name="org.apereo.cas" level="info" additivity="false">
<AppenderRef ref="console" />
<AppenderRef ref="file" />
</AsyncLogger>
<AsyncRoot level="warn">
<AppenderRef ref="console" />
</AsyncRoot>
</Loggers>
</Configuration>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
四、审计日志体系
4.1 cas_audit.log 的作用
审计日志(Audit Log)是 CAS 安全体系的核心组成部分。与普通应用日志不同,审计日志记录的是安全相关事件的不可篡改追踪记录,是安全合规(如等保2.0、ISO 27001、SOC 2)的基础设施。
在我们的实际项目中,审计日志通过 log4j2.xml 中的 auditlogfile Appender 输出到独立的 cas_audit.log 文件:
xml
<RollingFile name="auditlogfile" fileName="${baseDir}/cas_audit.log"
append="true"
filePattern="${baseDir}/cas_audit-%d{yyyy-MM-dd-HH}-%i.log">
<PatternLayout pattern="%d %p [%c] - %m%n" />
<Policies>
<OnStartupTriggeringPolicy />
<SizeBasedTriggeringPolicy size="10 MB" />
<TimeBasedTriggeringPolicy />
</Policies>
<DefaultRolloverStrategy max="5" compressionLevel="9">
<Delete basePath="${baseDir}" maxDepth="2">
<IfFileName glob="*/*.log.gz" />
<IfLastModified age="7d" />
</Delete>
</DefaultRolloverStrategy>
</RollingFile>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
审计日志的 Logger 配置为:
xml
<AsyncLogger name="org.apereo.inspektr.audit.support" level="info"
includeLocation="true">
<AppenderRef ref="casAudit" />
<AppenderRef ref="casFile" />
</AsyncLogger>1
2
3
4
5
2
3
4
5
注意这里使用了 org.apereo.inspektr.audit.support 作为 Logger 名称,这是 CAS 使用的审计框架 -- Inspektr 的包路径。Inspektr 是 Apereo 生态中的通用审计框架,提供了声明式的审计日志记录能力。
审计日志与应用日志的关键差异:
| 维度 | 应用日志 (cas.log) | 审计日志 (cas_audit.log) |
|---|---|---|
| 目的 | 故障排查、运行监控 | 安全审计、合规检查 |
| 内容 | 异常堆栈、调试信息 | 认证事件、授权决策 |
| 格式 | 可变、包含技术细节 | 结构化、便于解析 |
| 保留周期 | 30天 | 180天+ |
| 访问控制 | 开发团队 | 安全管理员 |
| 修改限制 | 可调整 | 不可篡改 |
4.2 审计日志格式
CAS 的审计日志由 Inspektr 框架生成,采用结构化的格式记录每一次安全事件。典型的审计日志格式如下:
2024-01-15 10:30:45,123 INFO [org.apereo.inspektr.audit.support] -
[AUDIT] ...who=admin...what=AUTHENTICATION_SUCCESS...when=2024-01-15T10:30:45.123Z...
action=AUTHENTICATION_SUCCESS...application=CAS...clientIp=192.168.1.100...
serverIp=10.0.0.1...userAgent=Mozilla/5.0...geoLocation=CN-BJ...
resource=https://cas.example.com/cas/login...ticket=ST-1-xxxx...1
2
3
4
5
2
3
4
5
审计日志字段解析:
| 字段 | 描述 | 示例值 |
|---|---|---|
who | 执行操作的用户 | admin |
what | 操作类型 | AUTHENTICATION_SUCCESS |
when | 操作时间(ISO 8601) | 2024-01-15T10:30:45.123Z |
action | 动作描述 | AUTHENTICATION_SUCCESS |
application | 应用标识 | CAS |
clientIp | 客户端 IP 地址 | 192.168.1.100 |
serverIp | 服务器 IP 地址 | 10.0.0.1 |
userAgent | 用户代理字符串 | Mozilla/5.0... |
resource | 请求资源路径 | https://cas.example.com/cas/login |
ticket | 关联的票据 ID | ST-1-xxxx |
4.3 审计事件类型
CAS 的审计日志覆盖了认证链路中的所有关键事件。以下是主要的审计事件类型及其业务含义:
认证事件:
| 事件类型 | 描述 | 触发时机 |
|---|---|---|
AUTHENTICATION_SUCCESS | 认证成功 | 用户凭证验证通过 |
AUTHENTICATION_FAILED | 认证失败 | 用户凭证验证失败 |
AUTHENTICATION_FAILED_BAD_CREDENTIALS | 凭证错误 | 用户名或密码不正确 |
AUTHENTICATION_FAILED_INVALID_REQUEST | 无效请求 | 请求格式不正确 |
AUTHENTICATION_FAILED_UNAUTHORIZED | 未授权 | 用户无权访问 |
票据事件:
| 事件类型 | 描述 | 触发时机 |
|---|---|---|
TICKET_GRANTING_TICKET_CREATED | TGT 创建 | 用户首次认证成功 |
TICKET_GRANTING_TICKET_DESTROYED | TGT 销毁 | 用户登出或 TGT 过期 |
SERVICE_TICKET_CREATED | ST 创建 | 应用请求服务票据 |
SERVICE_TICKET_VALIDATED | ST 验证 | 应用验证服务票据 |
PROXY_TICKET_CREATED | PT 创建 | 代理票据签发 |
服务访问事件:
| 事件类型 | 描述 | 触发时机 |
|---|---|---|
SERVICE_ACCESS_APPROVED | 服务访问批准 | 已注册服务请求访问 |
SERVICE_ACCESS_DENIED | 服务访问拒绝 | 未注册或被拒绝的服务 |
UNAUTHORIZED_SERVICE_PROXY_GRANTING_TICKET | 未授权代理 | 代理票据签发被拒绝 |
配置相关事件:
| 事件类型 | 描述 | 触发时机 |
|---|---|---|
CREATE_SERVICE | 创建服务 | 管理员注册新服务 |
DELETE_SERVICE | 删除服务 | 管理员删除服务 |
UPDATE_SERVICE | 更新服务 | 管理员修改服务配置 |
4.4 审计日志与安全合规
审计日志是企业安全合规体系的重要支撑。以下是 CAS 审计日志在常见合规标准中的映射关系:
等保2.0(GB/T 22239-2019)要求:
| 控制项 | CAS 审计日志对应能力 |
|---|---|
| 安全审计 -- 记录用户登录/注销 | AUTHENTICATION_SUCCESS/FAILED 事件 |
| 安全审计 -- 记录重要操作 | SERVICE_TICKET_CREATED 等票据事件 |
| 安全审计 -- 审计记录保护 | append="true" + 文件权限控制 |
| 安全审计 -- 审计记录保存 | 建议保留 180 天以上 |
GDPR 合规要求:
| 控制项 | CAS 审计日志对应能力 |
|---|---|
| 数据处理记录 | 审计日志记录所有用户数据处理行为 |
| 数据主体访问权 | 通过审计日志查询用户数据访问历史 |
| 数据保护影响评估 | 审计日志支持 DPIA 所需的数据流分析 |
审计日志安全加固建议:
- 文件权限控制: 审计日志文件应设置为仅追加模式,且仅安全管理员有读取权限。
bash
# 设置审计日志目录权限
chown cas:cas /var/log/cas
chmod 750 /var/log/cas
# 使用 chattr 设置不可变属性(Linux)
chattr +a /var/log/cas/cas_audit.log1
2
3
4
5
6
2
3
4
5
6
- 日志完整性校验: 定期对审计日志进行哈希校验,确保日志未被篡改。
bash
# 生成日志文件哈希
sha256sum /var/log/cas/cas_audit.log > /var/log/cas/cas_audit.log.sha256
# 校验日志完整性
sha256sum -c /var/log/cas/cas_audit.log.sha2561
2
3
4
5
2
3
4
5
日志远程备份: 通过 Filebeat/Fluentd 将审计日志实时传输到远程日志中心,防止本地日志被删除。
敏感信息过滤: 审计日志中不应包含密码、Token 等敏感信息。CAS 默认会对密码进行脱敏处理,但建议额外检查自定义字段。
五、性能统计日志
5.1 perfStats.log 的作用
性能统计日志是 CAS 可观测性体系中的第三支柱,专注于记录认证链路中各关键操作的耗时指标。通过分析性能日志,可以建立 CAS 系统的性能基线,识别性能瓶颈,并为容量规划提供数据支撑。
在我们的实际项目中,性能日志通过 perfFileAppender 输出到 perfStats.log:
xml
<RollingFile name="perfFileAppender" fileName="${baseDir}/perfStats.log"
append="true"
filePattern="${baseDir}/perfStats-%d{yyyy-MM-dd-HH}-%i.log">
<PatternLayout pattern="%m%n" />
<Policies>
<OnStartupTriggeringPolicy />
<SizeBasedTriggeringPolicy size="10 MB" />
<TimeBasedTriggeringPolicy />
</Policies>
<DefaultRolloverStrategy max="5" compressionLevel="9">
<Delete basePath="${baseDir}" maxDepth="2">
<IfFileName glob="*/*.log.gz" />
<IfLastModified age="7d" />
</Delete>
</DefaultRolloverStrategy>
</RollingFile>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
性能日志的 Logger 配置为:
xml
<AsyncLogger name="perfStatsLogger" level="info" additivity="false"
includeLocation="true">
<AppenderRef ref="casPerf" />
</AsyncLogger>1
2
3
4
2
3
4
性能日志的设计特点:
PatternLayout pattern="%m%n":仅输出消息本身,不包含时间戳和日志级别。这是因为 CAS 的性能记录器内部已经格式化了完整的统计信息。additivity="false":性能日志不向根 Logger 传播,避免性能数据污染应用日志。- 独立的 RollingFile:性能日志使用独立的滚动策略,可以设置与应用日志不同的保留周期。
5.2 票据处理耗时统计
CAS 的性能日志记录了票据生命周期中各关键操作的耗时。典型的票据处理性能日志如下:
[2024-01-15T10:30:45.123Z] ACTION=TICKET_GRANTING_TICKET_CREATED
TICKET=TGT-1-xxxxxxxxxxxxxxxxxxxxx
CLIENT_IP=192.168.1.100
PRINCIPAL=admin
AUTHENTICATION_HANDLER=DatabaseAuthenticationHandler
AUTHENTICATION_DURATION=125ms
TICKET_CREATION_DURATION=8ms
TOTAL_DURATION=133ms1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
票据处理的关键耗时指标:
| 指标 | 描述 | 正常范围 | 告警阈值 |
|---|---|---|---|
| AUTHENTICATION_DURATION | 认证处理器耗时 | 10-100ms | > 500ms |
| TICKET_CREATION_DURATION | 票据创建耗时 | 1-10ms | > 50ms |
| TICKET_VALIDATION_DURATION | 票据验证耗时 | 1-5ms | > 20ms |
| TOTAL_DURATION | 总处理耗时 | 15-150ms | > 1000ms |
票据处理耗时分析示例:
# 使用 awk 统计平均认证耗时
awk '/AUTHENTICATION_DURATION/ {
match($0, /AUTHENTICATION_DURATION=([0-9]+)ms/, arr);
sum += arr[1]; count++
} END {
print "Average Authentication Duration:", sum/count, "ms"
}' perfStats.log
# 使用 grep 筛选慢认证请求
grep 'AUTHENTICATION_DURATION=[5-9][0-9][0-9]ms\|AUTHENTICATION_DURATION=[0-9][0-9][0-9][0-9]ms' perfStats.log1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
5.3 认证处理器性能统计
CAS 支持多种认证处理器(Authentication Handler),每种处理器的性能特征不同。性能日志可以帮助识别不同认证处理器的性能瓶颈。
常见认证处理器性能对比:
| 认证处理器 | 典型耗时 | 性能影响因素 |
|---|---|---|
AcceptUsersAuthenticationHandler | < 1ms | 内存操作,无外部依赖 |
QueryDatabaseAuthenticationHandler | 10-100ms | 数据库查询性能、连接池配置 |
LdapAuthenticationHandler | 20-200ms | LDAP 服务器响应时间、网络延迟 |
RadiusAuthenticationHandler | 50-300ms | RADIUS 服务器响应时间 |
RestAuthenticationHandler | 50-500ms | REST API 响应时间、网络延迟 |
OAuth20AuthenticationHandler | 100-1000ms | OAuth 提供商响应时间 |
认证处理器性能监控配置:
在我们的项目中,CAS 5.3 使用数据库认证(通过 MyBatis + MySQL),性能日志中会记录每次数据库认证的耗时。如果发现 AUTHENTICATION_DURATION 持续偏高,可以从以下方面排查:
- 数据库连接池配置: 检查连接池是否已满,是否存在连接等待。
- SQL 查询优化: 检查用户查询 SQL 是否使用了合适的索引。
- 数据库服务器负载: 检查数据库服务器的 CPU、内存、磁盘 I/O 使用率。
- 网络延迟: 检查 CAS 服务器与数据库服务器之间的网络延迟。
5.4 性能基线建立
性能基线(Performance Baseline)是系统容量规划和性能异常检测的基础。通过持续收集和分析性能日志,可以建立 CAS 系统的性能基线。
建立性能基线的步骤:
- 数据收集阶段(1-2 周): 在正常业务负载下持续收集性能日志。
- 统计分析阶段: 计算各指标的平均值、P50、P90、P95、P99 分位数。
- 基线确定阶段: 以 P95 值作为性能基线,P99 值作为告警阈值。
- 持续监控阶段: 将实时性能数据与基线对比,识别异常波动。
性能基线示例:
CAS 认证性能基线(2024年1月,基于 100 万次认证请求统计)
================================================================
指标 平均值 P50 P90 P95 P99
----------------------------------------------------------------
AUTHENTICATION_DURATION 45ms 38ms 85ms 120ms 350ms
TICKET_CREATION_DURATION 3ms 2ms 5ms 8ms 15ms
TGT_CREATION_TOTAL 55ms 45ms 95ms 135ms 400ms
ST_CREATION_TOTAL 12ms 10ms 18ms 25ms 50ms
ST_VALIDATION_TOTAL 8ms 6ms 12ms 18ms 35ms
----------------------------------------------------------------1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
基于性能基线的告警规则:
yaml
# Grafana 告警规则示例
groups:
- name: cas_performance
rules:
- alert: CASAuthenticationSlow
expr: cas_auth_duration_p95 > 200
for: 5m
labels:
severity: warning
annotations:
summary: "CAS 认证耗时超过基线阈值"
description: "P95 认证耗时 {{ $value }}ms,超过基线 200ms"1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
六、Spring Boot Actuator 集成
6.1 5.3 版本:endpoints.enabled=false
CAS 5.3 基于 Spring Boot 1.x/2.x,Actuator 端点的配置方式与现代版本有显著差异。在我们的实际项目中,CAS 5.3 采用了默认关闭所有端点、仅按需开放的安全策略。
CAS 5.3 实际项目配置:
yaml
# Spring Boot 端点配置(禁用核心管控端点)
endpoints:
enabled: false
sensitive: true
restart:
enabled: false
shutdown:
enabled: false
# 监控/Actuator 端点安全配置
management:
security:
enabled: true
roles: ACTUATOR,ADMIN
sessions: if_required
# 监控端点上下文路径
context-path: /status
# 禁用应用上下文头
add-application-context-header: false
# 健康状态优先级定义
health:
status:
order: WARN, DOWN, OUT_OF_SERVICE, UNKNOWN, UP
# Spring Security 基础认证配置
security:
basic:
authorize-mode: role
# 监控端点路径保护
path: /cas/status/**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
配置深度解析:
endpoints.enabled: false:全局禁用所有 Actuator 端点。这是一个非常保守但安全的默认策略。在生产环境中,即使攻击者发现了 Actuator 端点的路径,也无法获取任何系统信息。endpoints.sensitive: true:将所有端点标记为敏感。在 Spring Boot 1.x 中,敏感端点需要认证后才能访问。endpoints.restart.enabled: false和endpoints.shutdown.enabled: false:显式禁用重启和关闭端点。这两个端点具有极高的安全风险,即使在开发环境中也应谨慎开放。management.security.roles: ACTUATOR,ADMIN:定义可以访问 Actuator 端点的角色。只有拥有ACTUATOR或ADMIN角色的用户才能访问监控端点。management.context-path: /status:将 Actuator 端点的上下文路径从默认的/actuator修改为/status。这是一种安全加固措施,可以增加攻击者枚举端点的难度。security.basic.path: /cas/status/**:通过 Spring Security 的 Basic 认证保护监控端点路径。访问/cas/status/**路径下的所有资源都需要提供用户名和密码。
CAS 5.3 Actuator 端点访问示例:
bash
# 访问健康检查端点(需要认证)
curl -u actuator:password https://cas.example.com:8443/cas/status/health
# 响应示例
{
"status": "UP",
"diskSpace": {
"status": "UP",
"total": 107374182400,
"free": 53687091200,
"threshold": 10485760
},
"redis": {
"status": "UP",
"version": "6.2.6"
}
}1
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
6.2 6.6 版本:management.endpoints.web.exposure.include
CAS 6.6 基于 Spring Boot 2.x,Actuator 端点的配置方式进行了现代化重构。最显著的变化是引入了 management.endpoints.web.exposure.include/exclude 机制,替代了旧版的 endpoints.enabled 配置。
CAS 6.6 实际项目配置:
yaml
# ====================== 监控端点配置(通用配置) ======================
management:
endpoints:
enabled-by-default: false
web:
exposure:
include: health,info
exclude: refresh,shutdown
base-path: /status
endpoint:
restart:
enabled: false
shutdown:
enabled: false
health:
enabled: true
show-details: when_authorized
info:
enabled: true
security:
enabled: true
roles: ACTUATOR,ADMIN
session:
creation-policy: if_required
context-path: /status
add-application-context-header: false
health:
status:
order: WARN, DOWN, OUT_OF_SERVICE, UNKNOWN, UP1
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
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
配置深度解析:
management.endpoints.enabled-by-default: false:默认禁用所有端点。与 CAS 5.3 的endpoints.enabled: false效果相同,但配置路径发生了变化。management.endpoints.web.exposure.include: health,info:仅暴露health和info两个端点。这是一种最小权限原则的体现,仅开放运维必需的端点。management.endpoints.web.exposure.exclude: refresh,shutdown:显式排除refresh和shutdown端点。即使include中包含了通配符,这两个端点也不会被暴露。management.endpoints.web.base-path: /status:设置 Actuator 端点的基础路径。在 CAS 6.6 中,这个配置替代了旧版的management.context-path。management.endpoint.health.show-details: when_authorized:健康检查详情仅在授权用户访问时显示。匿名用户只能看到{"status": "UP"}的简化响应,而授权用户可以看到各组件的详细健康状态。management.endpoint.restart.enabled: false和management.endpoint.shutdown.enabled: false:显式禁用危险端点,提供双重保障。
CAS 6.6 与 5.3 的端点配置对比:
| 配置项 | CAS 5.3 | CAS 6.6 |
|---|---|---|
| 全局禁用 | endpoints.enabled: false | management.endpoints.enabled-by-default: false |
| 端点暴露 | 无显式控制 | management.endpoints.web.exposure.include |
| 端点排除 | 无显式控制 | management.endpoints.web.exposure.exclude |
| 基础路径 | management.context-path: /status | management.endpoints.web.base-path: /status |
| 健康详情 | 默认显示 | show-details: when_authorized |
| 安全角色 | management.security.roles | management.security.roles |
6.3 7.3 版本:management.endpoints.web.exposure.include
CAS 7.3 基于 Spring Boot 3.x,Actuator 端点配置在 6.6 的基础上进一步精简。
CAS 7.3 实际项目配置:
yaml
# ====================== 监控端点配置 ======================
management:
endpoints:
web:
exposure:
include: health,info
exclude: refresh,shutdown
base-path: /status
endpoint:
health:
enabled: true
show-details: when_authorized
info:
enabled: true
security:
enabled: true
roles: ACTUATOR,ADMIN
session:
creation-policy: if_required
context-path: /status
add-application-context-header: false
health:
status:
order: WARN, DOWN, OUT_OF_SERVICE, UNKNOWN, UP1
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 7.3 与 6.6 的关键差异:
移除了
enabled-by-default: false: CAS 7.3 不再需要显式设置此选项,因为include: health,info已经隐式地限制了端点暴露范围。未在include中列出的端点默认不会被暴露。移除了
restart.enabled和shutdown.enabled: CAS 7.3 中这些危险端点默认不暴露,不需要显式禁用。exclude: refresh,shutdown已经提供了足够的保护。management.security配置保持不变: 角色控制策略在三个版本中保持一致,都使用ACTUATOR,ADMIN角色。
CAS 7.3 Actuator 端点访问示例:
bash
# 匿名访问健康检查(仅显示状态摘要)
curl https://cas.example.com:8443/status/health
# 响应:{"status": "UP"}
# 授权访问健康检查(显示详细信息)
curl -u admin:password https://cas.example.com:8443/status/health
# 响应:
{
"status": "UP",
"components": {
"diskSpace": {
"status": "UP",
"details": {
"total": 107374182400,
"free": 53687091200,
"threshold": 10485760
}
},
"redis": {
"status": "UP",
"details": {
"version": "7.0.12"
}
},
"pingHealthIndicator": {
"status": "UP"
}
}
}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
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
6.4 健康检查配置
健康检查是 Actuator 端点中最常用的功能,也是负载均衡器、Kubernetes 探针等基础设施依赖的关键接口。CAS 在三个版本中都配置了自定义的健康状态排序。
健康状态排序配置:
yaml
management:
health:
status:
order: WARN, DOWN, OUT_OF_SERVICE, UNKNOWN, UP1
2
3
4
2
3
4
这个配置定义了健康状态的严重程度排序。当多个组件报告不同的健康状态时,CAS 会按照这个排序选择最严重的状态作为整体状态。
CAS 自动注册的健康指标:
| 健康指标 | 描述 | 依赖 |
|---|---|---|
diskSpace | 磁盘空间检查 | 内置 |
redis | Redis 连接检查 | cas-server-support-redis-ticket-registry |
dataSource | 数据库连接检查 | spring-boot-starter-jdbc |
mail | 邮件服务检查 | spring-boot-starter-mail |
ldap | LDAP 连接检查 | cas-server-support-ldap |
cas | CAS 核心组件检查 | 内置 |
健康检查端点在 Kubernetes 中的使用:
yaml
# Kubernetes Deployment 配置
apiVersion: apps/v1
kind: Deployment
metadata:
name: cas-server
spec:
template:
spec:
containers:
- name: cas
image: cas-server:latest
ports:
- containerPort: 8443
livenessProbe:
httpGet:
scheme: HTTPS
path: /status/health
port: 8443
initialDelaySeconds: 120
periodSeconds: 30
failureThreshold: 3
readinessProbe:
httpGet:
scheme: HTTPS
path: /status/health
port: 8443
initialDelaySeconds: 60
periodSeconds: 10
failureThreshold: 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
29
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
6.5 管理端点安全
CAS 的 Actuator 端点安全策略在三个版本中保持一致,都基于 Spring Security 的角色认证机制。
安全配置的核心要素:
角色定义:
management.security.roles: ACTUATOR,ADMIN。只有拥有这两个角色之一的用户才能访问监控端点。路径保护: 在 CAS 5.3 中通过
security.basic.path: /cas/status/**实现路径级别的保护。在 CAS 6.6/7.3 中,Spring Boot 自动保护 Actuator 端点路径。会话策略:
management.session.creation-policy: if_required。仅在需要时创建会话,避免不必要的会话创建开销。
CAS 5.3 中的用户配置:
在 CAS 5.3 中,Actuator 端点的认证用户通常在 application.yml 或单独的安全配置文件中定义:
yaml
# CAS 5.3 Actuator 用户配置(示例)
security:
user:
name: actuator
password: ${ACTUATOR_PASSWORD:changeme}
role: ACTUATOR,ADMIN1
2
3
4
5
6
2
3
4
5
6
生产环境安全加固建议:
- 使用强密码: Actuator 端点的密码应通过环境变量注入,不硬编码在配置文件中。
bash
# 通过环境变量设置密码
export ACTUATOR_PASSWORD=$(openssl rand -base64 32)1
2
2
启用 HTTPS: Actuator 端点必须通过 HTTPS 访问,防止密码在网络中明文传输。
IP 白名单: 在反向代理层(如 Nginx)配置 IP 白名单,限制 Actuator 端点的访问来源。
nginx
# Nginx 配置示例
location /cas/status/ {
allow 10.0.0.0/8;
allow 192.168.0.0/16;
deny all;
proxy_pass https://cas-backend:8443;
}1
2
3
4
5
6
7
2
3
4
5
6
7
- 审计 Actuator 访问: 记录所有 Actuator 端点的访问日志,便于安全审计。
6.6 自定义健康指标
除了 CAS 自动注册的健康指标外,还可以通过编程方式添加自定义健康指标,用于监控业务相关的系统状态。
自定义健康指标示例:
java
package cc.bima.cas.health;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
/**
* 自定义健康指标:检查用户中心数据库连接
*/
@Component
public class UserCenterHealthIndicator implements HealthIndicator {
private final UserCenterService userCenterService;
public UserCenterHealthIndicator(UserCenterService userCenterService) {
this.userCenterService = userCenterService;
}
@Override
public Health health() {
try {
// 检查用户中心服务是否可用
boolean isAvailable = userCenterService.checkConnection();
if (isAvailable) {
return Health.up()
.withDetail("userCenter", "available")
.withDetail("responseTime", userCenterService.getLastResponseTime() + "ms")
.build();
} else {
return Health.down()
.withDetail("userCenter", "unavailable")
.withDetail("error", "Connection timeout")
.build();
}
} catch (Exception e) {
return Health.down()
.withDetail("userCenter", "error")
.withDetail("exception", e.getClass().getSimpleName())
.build();
}
}
}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
自定义健康指标在健康检查中的表现:
json
{
"status": "UP",
"components": {
"userCenter": {
"status": "UP",
"details": {
"userCenter": "available",
"responseTime": "15ms"
}
},
"redis": {
"status": "UP"
}
}
}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
七、日志收集与分析
7.1 ELK Stack 集成
ELK Stack(Elasticsearch + Logstash + Kibana)是日志收集与分析领域最经典的解决方案。将 CAS 的三日志体系接入 ELK,可以实现集中化的日志管理、全文检索和可视化分析。
架构设计:
CAS Server → Filebeat → Logstash → Elasticsearch → Kibana
↓
Redis/Kafka(缓冲)1
2
3
2
3
Filebeat 配置示例:
yaml
# filebeat.yml
filebeat.inputs:
# CAS 应用日志
- type: log
enabled: true
paths:
- /var/log/cas/cas.log
fields:
log_type: cas_application
app: cas-server
env: production
multiline:
pattern: '^\d{4}-\d{2}-\d{2}'
negate: true
match: after
# CAS 审计日志
- type: log
enabled: true
paths:
- /var/log/cas/cas_audit.log
fields:
log_type: cas_audit
app: cas-server
env: production
# CAS 性能日志
- type: log
enabled: true
paths:
- /var/log/cas/perfStats.log
fields:
log_type: cas_performance
app: cas-server
env: production
output.logstash:
hosts: ["logstash.example.com:5044"]
loadbalance: true
bulk_max_size: 20481
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
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
Logstash 过滤配置示例:
ruby
# logstash-cas.conf
input {
beats {
port => 5044
}
}
filter {
if [fields][log_type] == "cas_audit" {
grok {
match => {
"message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} \[%{DATA:logger}\] - %{GREEDYDATA:audit_message}"
}
}
kv {
source => "audit_message"
field_split => "..."
value_split => "="
prefix => "audit_"
}
mutate {
remove_field => ["message", "audit_message"]
}
}
if [fields][log_type] == "cas_performance" {
grok {
match => {
"message" => "\[%{TIMESTAMP_ISO8601:timestamp}\] ACTION=%{WORD:action} TICKET=%{DATA:ticket} AUTHENTICATION_DURATION=%{INT:auth_duration}ms"
}
}
}
}
output {
elasticsearch {
hosts => ["http://elasticsearch.example.com:9200"]
index => "cas-%{[fields][log_type]}-%{+YYYY.MM.dd}"
}
}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
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
Kibana 可视化建议:
- 认证趋势仪表板: 展示每小时认证成功/失败数量趋势图。
- 认证耗时分布图: 展示认证耗时的 P50/P90/P99 分位数变化趋势。
- TOP 10 失败用户: 展示认证失败次数最多的用户列表。
- 服务访问热力图: 展示各服务的访问频率和时间分布。
- 实时告警面板: 展示最近的异常事件和告警信息。
7.2 Loki + Grafana
Loki + Grafana 是近年来兴起的轻量级日志收集方案,与 ELK 相比具有更低的资源消耗和更简单的运维成本。
架构设计:
CAS Server → Promtail → Loki → Grafana1
Promtail 配置示例:
yaml
# promtail-config.yml
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki.example.com:3100/loki/api/v1/push
scrape_configs:
- job_name: cas-logs
static_configs:
- targets:
- localhost
labels:
job: cas-server
env: production
__path__: /var/log/cas/*.log
pipeline_stages:
- json:
expressions:
level: level
logger: logger
timestamp: timestamp
- labels:
level:
logger:
- timestamp:
source: timestamp
format: RFC33391
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
Grafana 日志查询示例:
logql
# 查询最近 1 小时的认证失败日志
{job="cas-server", filename="/var/log/cas/cas_audit.log"} |= "AUTHENTICATION_FAILED"
# 查询认证耗时超过 500ms 的记录
{job="cas-server", filename="/var/log/cas/perfStats.log"} |~ "AUTHENTICATION_DURATION=[5-9][0-9]{2}ms|AUTHENTICATION_DURATION=[0-9]{4}ms"
# 统计每分钟认证成功次数
sum(rate({job="cas-server", filename="/var/log/cas/cas_audit.log"} |= "AUTHENTICATION_SUCCESS" [1m]))1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
Loki vs ELK 选型建议:
| 维度 | ELK Stack | Loki + Grafana |
|---|---|---|
| 资源消耗 | 高(Elasticsearch 内存密集) | 低(Loki 仅索引标签) |
| 全文检索 | 强大 | 基本支持 |
| 运维复杂度 | 高 | 低 |
| 与监控集成 | 需要额外配置 | 原生集成 |
| 适用场景 | 复杂日志分析 | 日志+监控一体化 |
| 成本 | 高 | 低 |
7.3 阿里云日志服务
在国内云环境中,阿里云日志服务(SLS)是常用的日志收集方案。我们的实际项目中,CAS 5.3 的 build.gradle 和 pom.xml 都引入了阿里云日志 SDK 依赖:
groovy
// CAS 5.3 Gradle 构建配置
dependencies {
// 阿里云日志依赖
implementation 'com.aliyun.openservices:aliyun-log:0.6.56'
}1
2
3
4
5
2
3
4
5
xml
<!-- CAS 5.3 Maven 构建配置 -->
<dependency>
<groupId>com.aliyun.openservices</groupId>
<artifactId>aliyun-log</artifactId>
<version>0.6.56</version>
</dependency>1
2
3
4
5
6
2
3
4
5
6
Log4j2 Appender 集成阿里云日志服务:
阿里云日志服务提供了 Log4j2 的 Appender 实现,可以将日志直接发送到阿里云 SLS:
xml
<!-- log4j2.xml 中添加阿里云日志 Appender -->
<Appenders>
<!-- 阿里云日志服务 Appender -->
<AliyunLog name="aliyunLog"
projectName="cas-prod"
logStoreName="cas-logs"
endpoint="cn-beijing.log.aliyuncs.com"
accessKeyId="${env:ALIYUN_AK_ID}"
accessKeySecret="${env:ALIYUN_AK_SECRET}"
timeFormat="yyyy-MM-dd'T'HH:mm:ss.SSSZ"
timeZone="UTC">
<PatternLayout pattern="%d %p [%c] - %m%n" />
</AliyunLog>
</Appenders>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
阿里云日志服务配置最佳实践:
- 日志库分离: 为应用日志、审计日志、性能日志创建不同的 Logstore,便于独立设置保留策略和查询权限。
- 通过环境变量管理密钥: AccessKey ID 和 Secret 通过环境变量注入,不硬编码在配置文件中。
- 使用 RAM 角色: 在 ECS/K8s 环境中,推荐使用 ECS RAM 角色或 K8s OIDC 角色,避免使用静态 AccessKey。
- 设置合理的 Shard 数量: 根据日志量设置合适的 Shard 数量,通常每个 Shard 可以处理 5MB/s 的写入流量。
7.4 日志告警策略
日志告警是将被动日志分析转化为主动故障发现的关键机制。以下是针对 CAS 三日志体系的告警策略建议。
应用日志告警规则:
| 告警规则 | 条件 | 严重级别 | 响应时间 |
|---|---|---|---|
| CAS 启动失败 | CasWebApplication 出现 FATAL | P0 | 立即 |
| 认证异常 | AUTHENTICATION_FAILED 5分钟内超过 100 次 | P1 | 15分钟 |
| 票据存储异常 | Redis 连接失败 | P0 | 立即 |
| 内存溢出 | OutOfMemoryError | P0 | 立即 |
| 数据库连接异常 | Connection refused 或 Connection timeout | P1 | 15分钟 |
审计日志告警规则:
| 告警规则 | 条件 | 严重级别 | 响应时间 |
|---|---|---|---|
| 暴力破解检测 | 同一 IP 5分钟内认证失败超过 50 次 | P1 | 15分钟 |
| 异常时间登录 | 非工作时间的管理员登录 | P2 | 1小时 |
| 异常地域登录 | 非常用地区的登录请求 | P2 | 1小时 |
| 票据伪造嫌疑 | 不存在的 ST/TGT 被频繁验证 | P0 | 立即 |
性能日志告警规则:
| 告警规则 | 条件 | 严重级别 | 响应时间 |
|---|---|---|---|
| 认证耗时飙升 | P95 认证耗时超过 500ms 持续 5 分钟 | P1 | 15分钟 |
| 票据创建耗时异常 | TGT 创建耗时超过 200ms | P2 | 1小时 |
| Redis 响应缓慢 | Redis 操作耗时超过 100ms | P1 | 15分钟 |
Grafana 告警配置示例:
yaml
# grafana-alert-rules.yaml
apiVersion: 1
groups:
- name: cas-alerts
rules:
- uid: cas_auth_failure_alert
title: CAS 认证失败率过高
condition: C
data:
- refId: A
datasourceUid: loki
model:
expr: |
sum(rate({job="cas-server", filename="*/cas_audit.log"}
|= "AUTHENTICATION_FAILED" [5m]))
- refId: B
datasourceUid: __expr__
model:
type: threshold
expression: A
conditions:
- evaluator:
type: gt
params: [100]
reducer:
type: avg
operator:
type: and
- refId: C
datasourceUid: __expr__
model:
type: math
expression: $B > 1001
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
八、生产环境日志最佳实践
8.1 日志级别建议
生产环境的日志级别配置需要在可观测性和性能开销之间取得平衡。以下是我们在实际项目中总结的日志级别最佳实践。
核心原则:
- 默认保守,按需放开: 生产环境默认使用较高的日志级别(INFO/WARN),仅在排查特定问题时临时降低相关包的日志级别。
- 利用热更新能力: CAS 5.3/6.6 的
log4j2.xml支持monitorInterval="5"的热更新,可以在不重启服务的情况下调整日志级别。 - 分级分类控制: 不同类型的日志使用不同的级别策略。
推荐日志级别配置矩阵:
| 包/组件 | 开发环境 | 测试环境 | 生产环境 | 说明 |
|---|---|---|---|---|
org.apereo.cas | DEBUG | INFO | INFO | CAS 核心框架 |
org.apereo.cas.web.flow | DEBUG | DEBUG | INFO | Web Flow 流程 |
org.apereo.inspektr.audit | INFO | INFO | INFO | 审计日志 |
perfStatsLogger | INFO | INFO | INFO | 性能统计 |
org.springframework | DEBUG | INFO | WARN | Spring 框架 |
org.springframework.security | DEBUG | INFO | WARN | Spring Security |
org.springframework.web | DEBUG | INFO | WARN | Spring Web |
org.apache | DEBUG | WARN | ERROR | Apache 基础库 |
org.pac4j | DEBUG | WARN | OFF | PAC4J 认证库 |
org.opensaml | DEBUG | WARN | OFF | SAML 库 |
org.thymeleaf | DEBUG | WARN | OFF | 模板引擎 |
cc.bima.cas | DEBUG | INFO | INFO | 项目自定义代码 |
动态日志级别调整脚本:
bash
#!/bin/bash
# adjust-log-level.sh -- 动态调整 CAS 日志级别
# 用法: ./adjust-log-level.sh <package> <level>
PACKAGE=$1
LEVEL=$2
LOG4J2_XML="/etc/cas/config/log4j2.xml"
if [ -z "$PACKAGE" ] || [ -z "$LEVEL" ]; then
echo "Usage: $0 <package> <level>"
echo "Example: $0 org.apereo.cas DEBUG"
exit 1
fi
# 备份原配置
cp $LOG4J2_XML ${LOG4J2_XML}.bak.$(date +%Y%m%d%H%M%S)
# 使用 sed 修改日志级别
sed -i "s/<AsyncLogger name=\"${PACKAGE}\" level=\"[^\"]*\"/<AsyncLogger name=\"${PACKAGE}\" level=\"${LEVEL}\"/" $LOG4J2_XML
echo "Log level for ${PACKAGE} changed to ${LEVEL}"
echo "Change will take effect within 5 seconds (monitorInterval=5)"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
8.2 日志文件轮转策略
日志轮转策略直接影响磁盘空间的使用效率和日志管理的便捷性。以下是针对不同环境推荐的轮转策略。
开发环境轮转策略:
xml
<Policies>
<SizeBasedTriggeringPolicy size="10 MB" />
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
</Policies>
<DefaultRolloverStrategy max="3">
<Delete basePath="${baseDir}" maxDepth="1">
<IfLastModified age="3d" />
</Delete>
</DefaultRolloverStrategy>1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
- 文件大小:10MB(开发环境日志量小,便于查看)
- 时间间隔:每天轮转
- 最大文件数:3
- 保留天数:3天
生产环境轮转策略:
xml
<Policies>
<OnStartupTriggeringPolicy />
<SizeBasedTriggeringPolicy size="200 MB" />
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
</Policies>
<DefaultRolloverStrategy max="100">
<Delete basePath="${baseDir}" maxDepth="2">
<IfFileName glob="*.log.gz" />
<IfLastModified age="30d" />
</Delete>
</DefaultRolloverStrategy>1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
- 文件大小:200MB(减少文件碎片,提高 I/O 效率)
- 启动轮转:每次启动创建新文件
- 时间间隔:每天轮转
- 最大文件数:100
- 保留天数:30天
审计日志轮转策略(合规要求):
xml
<Policies>
<OnStartupTriggeringPolicy />
<SizeBasedTriggeringPolicy size="500 MB" />
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
</Policies>
<DefaultRolloverStrategy max="365">
<Delete basePath="${auditDir}" maxDepth="2">
<IfFileName glob="*.log.gz" />
<IfLastModified age="180d" />
</Delete>
</DefaultRolloverStrategy>1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
- 文件大小:500MB
- 保留天数:180天(满足等保2.0要求)
- 最大文件数:365
8.3 磁盘空间管理
日志文件是磁盘空间消耗的主要来源,需要建立完善的磁盘空间管理机制。
磁盘空间预算规划:
假设 CAS 集群每天处理 100 万次认证请求:
| 日志类型 | 单条大小 | 日志量/天 | 日增长量 | 30天总量 |
|---|---|---|---|---|
| 应用日志 | 200B | 500万行 | ~1GB | ~30GB |
| 审计日志 | 500B | 100万行 | ~500MB | ~15GB |
| 性能日志 | 150B | 100万行 | ~150MB | ~4.5GB |
| 合计 | - | - | ~1.65GB | ~49.5GB |
加上压缩归档(压缩率约 80%)和保留策略,实际磁盘占用约为 10-15GB/月。
磁盘空间监控脚本:
bash
#!/bin/bash
# check-log-disk.sh -- 检查日志目录磁盘使用情况
LOG_DIR="/var/log/cas"
WARN_THRESHOLD=80 # 警告阈值:80%
CRIT_THRESHOLD=90 # 严重阈值:90%
# 获取日志目录所在分区的使用率
DISK_USAGE=$(df -h ${LOG_DIR} | awk 'NR==2 {print $5}' | tr -d '%')
if [ "$DISK_USAGE" -ge "$CRIT_THRESHOLD" ]; then
echo "CRITICAL: Log disk usage at ${DISK_USAGE}%"
# 触发紧急清理:删除超过 7 天的归档日志
find ${LOG_DIR} -name "*.log.gz" -mtime +7 -delete
exit 2
elif [ "$DISK_USAGE" -ge "$WARN_THRESHOLD" ]; then
echo "WARNING: Log disk usage at ${DISK_USAGE}%"
# 发送告警通知
# curl -X POST ${ALERT_WEBHOOK} -d "Log disk usage at ${DISK_USAGE}%"
exit 1
else
echo "OK: Log disk usage at ${DISK_USAGE}%"
exit 0
fi1
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
Logrotate 集成(Linux 系统):
除了 Log4j2 自带的轮转策略外,还可以使用 Linux 系统的 logrotate 工具作为额外的保障:
# /etc/logrotate.d/cas-server
/var/log/cas/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
create 0640 cas cas
sharedscripts
postrotate
# CAS 使用 Log4j2 自带轮转,此处仅作为兜底
endscript
}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
8.4 敏感信息脱敏
CAS 作为认证系统,日志中不可避免地会包含用户名、IP 地址等敏感信息。在生产环境中,必须对日志中的敏感信息进行脱敏处理。
CAS 内置的脱敏机制:
CAS 框架内置了基本的敏感信息脱敏能力,主要通过以下方式实现:
- 密码脱敏: CAS 在记录认证日志时,会自动将密码替换为
******。 - Ticket 脱敏: 票据 ID 在日志中通常只显示前缀部分。
- CasAppender 拦截: CasAppender 可以在日志输出前进行自定义的脱敏处理。
自定义脱敏配置示例:
在 log4j2.xml 中使用 Log4j2 的 Rewrite Policy 实现脱敏:
xml
<Rewrite name="sanitizeRewrite">
<RewritePolicy>
<!-- 脱敏规则 -->
<PropertiesRewritePolicy>
<Property name="password" value="******" />
<Property name="credential" value="******" />
</PropertiesRewritePolicy>
</RewritePolicy>
</Rewrite>1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
自定义脱敏 Filter 实现:
java
package cc.bima.cas.logging;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Level;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.filter.AbstractFilter;
import org.apache.logging.log4j.message.Message;
/**
* 日志脱敏过滤器
* 对日志消息中的敏感信息进行替换
*/
public class SensitiveDataFilter extends AbstractFilter {
// 敏感信息正则模式
private static final Pattern PASSWORD_PATTERN =
Pattern.compile("(password|credential|secret)\\s*[=:>]\\s*['\"]?[^'\"\\s,}]+");
private static final Pattern PHONE_PATTERN =
Pattern.compile("(1[3-9]\\d)\\d{4}(\\d{4})");
private static final Pattern ID_CARD_PATTERN =
Pattern.compile "(\\d{6})\\d{8}(\\d{4})");
private static final Pattern EMAIL_PATTERN =
Pattern.compile("([a-zA-Z0-9._%+-]+)(@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})");
@Override
public Result filter(LogEvent event) {
Message message = event.getMessage();
if (message != null) {
String formatted = message.getFormattedMessage();
String sanitized = sanitize(formatted);
if (!formatted.equals(sanitized)) {
// 替换消息内容
// 注意:Log4j2 的消息不可变,需要通过其他方式实现
}
}
return Result.NEUTRAL;
}
private String sanitize(String input) {
String result = input;
result = PASSWORD_PATTERN.matcher(result).replaceAll("$1=******");
result = PHONE_PATTERN.matcher(result).replaceAll("$1****$2");
result = ID_CARD_PATTERN.matcher(result).replaceAll("$1********$2");
result = EMAIL_PATTERN.matcher(result).replaceAll("****$2");
return result;
}
}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
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
脱敏规则建议:
| 敏感信息类型 | 脱敏规则 | 示例 |
|---|---|---|
| 密码 | 完全替换 | password=****** |
| 手机号 | 中间四位替换 | 138****1234 |
| 身份证号 | 中间八位替换 | 110101********1234 |
| 邮箱 | 用户名替换 | ****@example.com |
| Token | 部分显示 | eyJhbGci...xxxx |
| IP 地址 | 最后一段替换 | 192.168.1.* |
8.5 分布式追踪(MDC)
在分布式部署环境中,一个用户请求可能经过多个 CAS 节点处理。MDC(Mapped Diagnostic Context)是实现分布式追踪的关键技术,可以在日志中自动关联同一请求的所有日志记录。
CAS 中的 MDC 使用:
CAS 框架内部已经集成了 MDC 支持,在认证流程中会自动设置以下 MDC 上下文变量:
| MDC Key | 描述 | 示例值 |
|---|---|---|
MDC_CLIENT_IP | 客户端 IP 地址 | 192.168.1.100 |
MDC_SERVER_IP | 服务器 IP 地址 | 10.0.0.1 |
MDC_TICKET_ID | 关联的票据 ID | TGT-1-xxxx |
MDC_PRINCIPAL | 认证主体 | admin |
MDC_SERVICE | 目标服务 | https://app.example.com |
MDC_USER_AGENT | 用户代理 | Mozilla/5.0... |
MDC_REQUEST_ID | 请求唯一标识 | req-abc123 |
在 log4j2.xml 中使用 MDC:
xml
<!-- 在 Pattern Layout 中引用 MDC 变量 -->
<PatternLayout pattern="%d %p [%c] [%X{MDC_REQUEST_ID}] [%X{MDC_CLIENT_IP}] - %m%n" />1
2
2
输出示例:
2024-01-15 10:30:45 INFO [o.a.c.web.flow] [req-abc123] [192.168.1.100] - Authentication successful for user admin
2024-01-15 10:30:45 INFO [o.a.c.web.flow] [req-abc123] [192.168.1.100] - TGT-1-xxxx created
2024-01-15 10:30:46 INFO [o.a.c.web.flow] [req-abc123] [192.168.1.100] - ST-1-yyyy granted for service https://app.example.com1
2
3
2
3
通过 MDC_REQUEST_ID,可以快速筛选出同一请求的所有日志记录,极大地提高了分布式环境下的故障排查效率。
自定义 MDC Filter:
如果需要添加自定义的 MDC 上下文信息,可以通过 Servlet Filter 实现:
java
package cc.bima.cas.filter;
import org.slf4j.MDC;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.UUID;
/**
* MDC 上下文过滤器
* 为每个请求生成唯一的追踪 ID 并设置到 MDC 中
*/
public class TraceMDCFilter implements Filter {
private static final String TRACE_ID = "traceId";
private static final String SPAN_ID = "spanId";
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
try {
// 从请求头获取或生成新的追踪 ID
String traceId = httpRequest.getHeader("X-Trace-Id");
if (traceId == null || traceId.isEmpty()) {
traceId = UUID.randomUUID().toString().replace("-", "");
}
String spanId = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
MDC.put(TRACE_ID, traceId);
MDC.put(SPAN_ID, spanId);
MDC.put("clientIp", httpRequest.getRemoteAddr());
MDC.put("requestUri", httpRequest.getRequestURI());
// 将追踪 ID 添加到响应头,便于下游服务传递
((HttpServletResponse) response).setHeader("X-Trace-Id", traceId);
chain.doFilter(request, response);
} finally {
MDC.remove(TRACE_ID);
MDC.remove(SPAN_ID);
MDC.remove("clientIp");
MDC.remove("requestUri");
}
}
}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
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
与 OpenTelemetry 集成:
在 CAS 7.3(基于 Spring Boot 3.x)中,可以集成 OpenTelemetry 实现标准的分布式追踪:
groovy
// CAS 7.3 Gradle 依赖
dependencies {
implementation 'io.opentelemetry:opentelemetry-api'
implementation 'io.opentelemetry:opentelemetry-sdk'
implementation 'io.opentelemetry.instrumentation:opentelemetry-spring-boot-starter'
}1
2
3
4
5
6
2
3
4
5
6
yaml
# application.yml OpenTelemetry 配置
otel:
exporter:
otlp:
endpoint: http://otel-collector.example.com:4317
resource:
attributes:
service.name: cas-server
deployment.environment: production
traces:
exporter: otlp
logs:
exporter: otlp1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
总结与展望
本文从 CAS Overlay 项目的实际配置出发,系统性地解析了 CAS 监控与日志体系的完整架构。让我们回顾一下各版本的核心配置要点:
版本配置要点总结:
| 维度 | CAS 5.3 | CAS 6.6 | CAS 7.3 |
|---|---|---|---|
| 日志配置 | log4j2.xml | log4j2.xml | application.yml |
| 异步日志 | AsyncLogger | AsyncLogger | Spring Boot 自动 |
| 三日志分离 | 支持 | 支持 | 需自定义 |
| Actuator 配置 | endpoints.enabled | exposure.include | exposure.include |
| 健康检查 | /status/health | /status/health | /status/health |
| 安全角色 | ACTUATOR,ADMIN | ACTUATOR,ADMIN | ACTUATOR,ADMIN |
| 日志热更新 | monitorInterval=5 | monitorInterval=5 | 需 Spring Cloud |
| Java 版本 | 1.8 | 11 | 17+ |
| Spring Boot | 1.x/2.x | 2.x | 3.x |
关键配置文件清单:
| 文件 | 版本 | 用途 |
|---|---|---|
log4j2.xml | 5.3/6.6 | Log4j2 日志配置 |
application.yml | 5.3/6.6/7.3 | Spring Boot 应用配置 |
build.gradle | 5.3/6.6/7.3 | Gradle 构建配置(日志依赖) |
pom.xml | 5.3/6.6/7.3 | Maven 构建配置(日志依赖) |
log4j2-spring.xml | 7.3(可选) | Log4j2 自定义配置 |
未来展望:
随着 CAS 版本的持续演进,日志和监控体系也在不断现代化。CAS 7.x 系列已经全面拥抱 Spring Boot 3.x 和 Jakarta EE,未来的版本将进一步深化与 OpenTelemetry、Micrometer 等可观测性标准的集成。对于企业级 CAS 部署,建议:
- 建立完善的日志规范: 制定统一的日志格式、级别、脱敏标准。
- 建设集中式日志平台: 基于 ELK 或 Loki+Grafana 建设统一的日志收集与分析平台。
- 实施主动监控告警: 基于日志和指标建立多层次的告警体系。
- 定期审计日志配置: 每季度审查日志级别、保留策略和安全配置。
- 关注版本演进: 及时跟进 CAS 新版本的可观测性特性,合理规划升级路径。
版权声明: 本文为必码(bima.cc)原创技术文章,仅供学习交流。
本文内容基于实际项目源码解析整理,代码示例均为教学简化版本,仅供学习参考。
文档内容提取自项目源码与配置文件,如需获取完整项目代码,请访问 bima.cc。