Skip to content

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.gradlepom.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'
}
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>

同时,在 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.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>

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: 30

版本迁移的关键差异点:

特性CAS 5.3/6.6 (XML)CAS 7.3 (YAML)
配置文件log4j2.xmlapplication.yml
异步日志AsyncLogger 显式配置Spring Boot 自动配置
自定义 AppenderCasAppender 支持需要编程式扩展
审计日志独立 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>

这四个 CasAppender 分别代理了审计日志文件、应用日志文件、控制台输出和性能统计日志文件。在 <Loggers> 配置中,所有的 <AsyncLogger> 都通过 <AppenderRef ref="casXxx" /> 引用 CasAppender,而非直接引用底层 Appender。

CasAppender 的实际价值:

  1. 日志上下文增强: CasAppender 可以在日志事件中自动注入 CAS 特有的上下文信息,如请求 ID、会话 ID、认证流水号等。
  2. 日志脱敏拦截: 在日志输出前对敏感信息(如密码、Token)进行自动脱敏处理。
  3. 日志路由控制: 根据运行环境(开发/测试/生产)动态调整日志输出策略。
  4. 审计日志增强: 为审计日志添加额外的安全元数据,如客户端 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>

<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 → 文件/控制台

业务线程仅需将日志事件放入 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>

关键属性解析:

  • 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.0

等待策略的选择:

等待策略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] - &lt;%m&gt;}%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. fileName="${baseDir}/cas.log":当前活跃日志文件的路径。${baseDir} 引用了 <Properties> 中定义的 baseDir 属性,值为 src/main/resources/etc/logs。在生产环境中,建议修改为 /var/log/cas/data/logs/cas 等标准日志目录。

  2. append="true":以追加模式写入日志文件。每次 CAS 重启后不会清空已有日志,这对于故障排查和日志连续性至关重要。

  3. filePattern="${baseDir}/cas-%d{yyyy-MM-dd-HH}-%i.log":归档日志文件的命名模式。%d{yyyy-MM-dd-HH} 表示按小时进行时间维度分割,%i 表示同小时内如果文件大小超限,使用递增序号区分。

  4. <Policies> 触发策略组合:

    • <OnStartupTriggeringPolicy />:CAS 服务启动时触发一次日志轮转。这确保了每次启动都有独立的日志文件,便于追踪每次启动后的行为。
    • <SizeBasedTriggeringPolicy size="10 MB" />:当当前日志文件达到 10MB 时触发轮转。这个值在开发环境中是合理的,但在生产高并发环境中,建议调整为 100MB-500MB,以减少日志文件碎片。
    • <TimeBasedTriggeringPolicy />:按时间周期触发轮转。配合 filePattern 中的 %d{yyyy-MM-dd-HH},实现每小时自动轮转。
  5. <DefaultRolloverStrategy max="5">:同时间窗口内最多保留 5 个归档文件(即 %i 的最大值为 5)。超过 5 个后,最早的归档文件将被删除。

  6. compressionLevel="9":使用 GZIP 压缩归档日志文件,压缩级别为 9(最高压缩率)。虽然压缩过程会消耗少量 CPU,但可以将日志文件体积压缩到原来的 10%-20%,显著节省磁盘空间。

  7. <Delete> 清理策略:

    xml
    <Delete basePath="${baseDir}" maxDepth="2">
        <IfFileName glob="*/*.log.gz" />
        <IfLastModified age="7d" />
    </Delete>
    • maxDepth="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 < OFF

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" />

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" />

第三方库日志级别:

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" />

根 Logger 配置:

xml
<AsyncRoot level="error">
    <AppenderRef ref="casConsole" />
</AsyncRoot>

根 Logger 的级别设置为 error,这意味着所有未被显式配置的 Logger 都只会输出 ERROR 级别及以上的日志。这是一个非常保守但安全的默认策略,可以有效控制日志量。

日志级别调优建议:

  • 开发环境:org.apereoorg.springframework 的级别调整为 debug,便于排查问题。
  • 测试环境: 保持 info 级别,但开启 org.apereo.cas.web.flowdebug 级别。
  • 生产环境: 严格按照上述配置,仅在排查特定问题时临时调整相关包的日志级别(利用 monitorInterval="5" 的热更新能力)。

2.5 日志格式 Pattern Layout

Pattern Layout 是 Log4j2 中控制日志输出格式的核心组件。CAS 的三个日志文件使用了不同的 Pattern Layout,体现了不同日志文件的用途差异。

控制台 Pattern Layout:

xml
<PatternLayout pattern="%d %p [%c] - &lt;%m&gt;%n" />

输出示例:

2024-01-15 10:30:45,678 INFO [org.apereo.cas.web.CasWebApplication] - <CAS Server started successfully>

应用日志文件 Pattern Layout(带高亮):

xml
<PatternLayout pattern="%highlight{%d %p [%c] - &lt;%m&gt;}%n" />

%highlight{} 是 Log4j2 提供的颜色高亮包装器,会根据日志级别自动添加 ANSI 颜色代码:

  • ERROR/FATAL:红色
  • WARN:黄色
  • INFO:绿色
  • DEBUG/TRACE:蓝色

注意:高亮仅在支持 ANSI 颜色的终端中有效,在日志文件中会显示为 ESC 转义序列。因此,高亮仅应用于控制台 Appender,不应用于文件 Appender(除非确定需要彩色日志文件)。

审计日志 Pattern Layout:

xml
<PatternLayout pattern="%d %p [%c] - %m%n" />

审计日志不使用 %highlight< > 包裹,保持纯文本格式,便于日志收集工具解析和审计系统对接。

性能统计日志 Pattern Layout:

xml
<PatternLayout pattern="%m%n" />

性能日志仅输出消息本身,因为 CAS 的性能记录器(perfStatsLogger)内部已经格式化了完整的统计信息,包括时间戳、操作类型、耗时等。

Pattern Layout 转换字符参考:

转换符描述示例输出
%d日期时间2024-01-15 10:30:45,678
%p日志级别INFO
%cLogger 名称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>

<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>

生产环境推荐配置:

xml
<Properties>
    <!-- 通过环境变量控制日志目录,提供默认值 -->
    <Property name="baseDir">${env:CAS_LOG_DIR:-/var/log/cas}</Property>
    <!-- 通过环境变量控制日志级别 -->
    <Property name="casLogLevel">${env:CAS_LOG_LEVEL:-info}</Property>
</Properties>

通过环境变量控制日志配置,可以在 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"

2.7 日志文件路径配置

日志文件路径的配置需要考虑开发环境和生产环境的差异。在我们的实际项目中,开发环境使用相对路径,生产环境使用绝对路径。

开发环境路径(CAS 5.3/6.6):

xml
<Property name="baseDir">src/main/resources/etc/logs</Property>

这个相对路径指向项目源码目录下的 etc/logs 文件夹,便于开发者在 IDE 中直接查看日志文件。

生产环境路径建议:

xml
<Property name="baseDir">${env:CAS_LOG_DIR:-/var/log/cas}</Property>

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 # 归档性能日志

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/cas

文件系统权限建议:

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/audit

三、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: WARN

与 CAS 5.3/6.6 的对比分析:

CAS 5.3/6.6 的 log4j2.xml 中定义了超过 30 个包级别的日志控制规则,而 CAS 7.3 的 YAML 配置仅保留了 5 条核心规则。这种简化反映了以下变化:

  1. Spring Boot 自动日志管理: Spring Boot 3.x 内置了智能的日志级别默认值,大部分第三方库的日志已经被合理地控制,不需要手动逐一配置。
  2. Log4j2 自动配置: Spring Boot 的 spring-boot-starter-log4j2 自动集成了 Log4j2,并通过 log4j2-spring.xml 或系统属性提供配置入口。
  3. 配置精简原则: 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: WARN

动态日志级别调整:

在 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.cas

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"

格式解析:

转换符描述示例输出
%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.ico

与 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: 30

配置项解析:

  • 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: true

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清理策略

迁移步骤:

  1. 移除 log4j2.xml 文件: CAS 7.3 不再使用独立的 log4j2.xml 文件。
  2. 在 application.yml 中添加 logging 配置: 按照上述格式添加基本的日志配置。
  3. 如需保留三日志分离: 创建 src/main/resources/log4j2-spring.xml 文件,Spring Boot 会自动识别并加载此文件。
  4. 如需保留 CasAppender:log4j2-spring.xml 中配置 packages="org.apereo.cas.logging"
  5. 验证日志输出: 启动 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>

四、审计日志体系

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>

审计日志的 Logger 配置为:

xml
<AsyncLogger name="org.apereo.inspektr.audit.support" level="info"
    includeLocation="true">
    <AppenderRef ref="casAudit" />
    <AppenderRef ref="casFile" />
</AsyncLogger>

注意这里使用了 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...

审计日志字段解析:

字段描述示例值
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关联的票据 IDST-1-xxxx

4.3 审计事件类型

CAS 的审计日志覆盖了认证链路中的所有关键事件。以下是主要的审计事件类型及其业务含义:

认证事件:

事件类型描述触发时机
AUTHENTICATION_SUCCESS认证成功用户凭证验证通过
AUTHENTICATION_FAILED认证失败用户凭证验证失败
AUTHENTICATION_FAILED_BAD_CREDENTIALS凭证错误用户名或密码不正确
AUTHENTICATION_FAILED_INVALID_REQUEST无效请求请求格式不正确
AUTHENTICATION_FAILED_UNAUTHORIZED未授权用户无权访问

票据事件:

事件类型描述触发时机
TICKET_GRANTING_TICKET_CREATEDTGT 创建用户首次认证成功
TICKET_GRANTING_TICKET_DESTROYEDTGT 销毁用户登出或 TGT 过期
SERVICE_TICKET_CREATEDST 创建应用请求服务票据
SERVICE_TICKET_VALIDATEDST 验证应用验证服务票据
PROXY_TICKET_CREATEDPT 创建代理票据签发

服务访问事件:

事件类型描述触发时机
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 所需的数据流分析

审计日志安全加固建议:

  1. 文件权限控制: 审计日志文件应设置为仅追加模式,且仅安全管理员有读取权限。
bash
# 设置审计日志目录权限
chown cas:cas /var/log/cas
chmod 750 /var/log/cas

# 使用 chattr 设置不可变属性(Linux)
chattr +a /var/log/cas/cas_audit.log
  1. 日志完整性校验: 定期对审计日志进行哈希校验,确保日志未被篡改。
bash
# 生成日志文件哈希
sha256sum /var/log/cas/cas_audit.log > /var/log/cas/cas_audit.log.sha256

# 校验日志完整性
sha256sum -c /var/log/cas/cas_audit.log.sha256
  1. 日志远程备份: 通过 Filebeat/Fluentd 将审计日志实时传输到远程日志中心,防止本地日志被删除。

  2. 敏感信息过滤: 审计日志中不应包含密码、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>

性能日志的 Logger 配置为:

xml
<AsyncLogger name="perfStatsLogger" level="info" additivity="false"
    includeLocation="true">
    <AppenderRef ref="casPerf" />
</AsyncLogger>

性能日志的设计特点:

  • 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=133ms

票据处理的关键耗时指标:

指标描述正常范围告警阈值
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.log

5.3 认证处理器性能统计

CAS 支持多种认证处理器(Authentication Handler),每种处理器的性能特征不同。性能日志可以帮助识别不同认证处理器的性能瓶颈。

常见认证处理器性能对比:

认证处理器典型耗时性能影响因素
AcceptUsersAuthenticationHandler< 1ms内存操作,无外部依赖
QueryDatabaseAuthenticationHandler10-100ms数据库查询性能、连接池配置
LdapAuthenticationHandler20-200msLDAP 服务器响应时间、网络延迟
RadiusAuthenticationHandler50-300msRADIUS 服务器响应时间
RestAuthenticationHandler50-500msREST API 响应时间、网络延迟
OAuth20AuthenticationHandler100-1000msOAuth 提供商响应时间

认证处理器性能监控配置:

在我们的项目中,CAS 5.3 使用数据库认证(通过 MyBatis + MySQL),性能日志中会记录每次数据库认证的耗时。如果发现 AUTHENTICATION_DURATION 持续偏高,可以从以下方面排查:

  1. 数据库连接池配置: 检查连接池是否已满,是否存在连接等待。
  2. SQL 查询优化: 检查用户查询 SQL 是否使用了合适的索引。
  3. 数据库服务器负载: 检查数据库服务器的 CPU、内存、磁盘 I/O 使用率。
  4. 网络延迟: 检查 CAS 服务器与数据库服务器之间的网络延迟。

5.4 性能基线建立

性能基线(Performance Baseline)是系统容量规划和性能异常检测的基础。通过持续收集和分析性能日志,可以建立 CAS 系统的性能基线。

建立性能基线的步骤:

  1. 数据收集阶段(1-2 周): 在正常业务负载下持续收集性能日志。
  2. 统计分析阶段: 计算各指标的平均值、P50、P90、P95、P99 分位数。
  3. 基线确定阶段: 以 P95 值作为性能基线,P99 值作为告警阈值。
  4. 持续监控阶段: 将实时性能数据与基线对比,识别异常波动。

性能基线示例:

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
----------------------------------------------------------------

基于性能基线的告警规则:

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"

六、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. endpoints.enabled: false:全局禁用所有 Actuator 端点。这是一个非常保守但安全的默认策略。在生产环境中,即使攻击者发现了 Actuator 端点的路径,也无法获取任何系统信息。

  2. endpoints.sensitive: true:将所有端点标记为敏感。在 Spring Boot 1.x 中,敏感端点需要认证后才能访问。

  3. endpoints.restart.enabled: falseendpoints.shutdown.enabled: false:显式禁用重启和关闭端点。这两个端点具有极高的安全风险,即使在开发环境中也应谨慎开放。

  4. management.security.roles: ACTUATOR,ADMIN:定义可以访问 Actuator 端点的角色。只有拥有 ACTUATORADMIN 角色的用户才能访问监控端点。

  5. management.context-path: /status:将 Actuator 端点的上下文路径从默认的 /actuator 修改为 /status。这是一种安全加固措施,可以增加攻击者枚举端点的难度。

  6. 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"
  }
}

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, UP

配置深度解析:

  1. management.endpoints.enabled-by-default: false:默认禁用所有端点。与 CAS 5.3 的 endpoints.enabled: false 效果相同,但配置路径发生了变化。

  2. management.endpoints.web.exposure.include: health,info:仅暴露 healthinfo 两个端点。这是一种最小权限原则的体现,仅开放运维必需的端点。

  3. management.endpoints.web.exposure.exclude: refresh,shutdown:显式排除 refreshshutdown 端点。即使 include 中包含了通配符,这两个端点也不会被暴露。

  4. management.endpoints.web.base-path: /status:设置 Actuator 端点的基础路径。在 CAS 6.6 中,这个配置替代了旧版的 management.context-path

  5. management.endpoint.health.show-details: when_authorized:健康检查详情仅在授权用户访问时显示。匿名用户只能看到 {"status": "UP"} 的简化响应,而授权用户可以看到各组件的详细健康状态。

  6. management.endpoint.restart.enabled: falsemanagement.endpoint.shutdown.enabled: false:显式禁用危险端点,提供双重保障。

CAS 6.6 与 5.3 的端点配置对比:

配置项CAS 5.3CAS 6.6
全局禁用endpoints.enabled: falsemanagement.endpoints.enabled-by-default: false
端点暴露无显式控制management.endpoints.web.exposure.include
端点排除无显式控制management.endpoints.web.exposure.exclude
基础路径management.context-path: /statusmanagement.endpoints.web.base-path: /status
健康详情默认显示show-details: when_authorized
安全角色management.security.rolesmanagement.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, UP

CAS 7.3 与 6.6 的关键差异:

  1. 移除了 enabled-by-default: false CAS 7.3 不再需要显式设置此选项,因为 include: health,info 已经隐式地限制了端点暴露范围。未在 include 中列出的端点默认不会被暴露。

  2. 移除了 restart.enabledshutdown.enabled CAS 7.3 中这些危险端点默认不暴露,不需要显式禁用。exclude: refresh,shutdown 已经提供了足够的保护。

  3. 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"
    }
  }
}

6.4 健康检查配置

健康检查是 Actuator 端点中最常用的功能,也是负载均衡器、Kubernetes 探针等基础设施依赖的关键接口。CAS 在三个版本中都配置了自定义的健康状态排序。

健康状态排序配置:

yaml
management:
  health:
    status:
      order: WARN, DOWN, OUT_OF_SERVICE, UNKNOWN, UP

这个配置定义了健康状态的严重程度排序。当多个组件报告不同的健康状态时,CAS 会按照这个排序选择最严重的状态作为整体状态。

CAS 自动注册的健康指标:

健康指标描述依赖
diskSpace磁盘空间检查内置
redisRedis 连接检查cas-server-support-redis-ticket-registry
dataSource数据库连接检查spring-boot-starter-jdbc
mail邮件服务检查spring-boot-starter-mail
ldapLDAP 连接检查cas-server-support-ldap
casCAS 核心组件检查内置

健康检查端点在 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: 3

6.5 管理端点安全

CAS 的 Actuator 端点安全策略在三个版本中保持一致,都基于 Spring Security 的角色认证机制。

安全配置的核心要素:

  1. 角色定义: management.security.roles: ACTUATOR,ADMIN。只有拥有这两个角色之一的用户才能访问监控端点。

  2. 路径保护: 在 CAS 5.3 中通过 security.basic.path: /cas/status/** 实现路径级别的保护。在 CAS 6.6/7.3 中,Spring Boot 自动保护 Actuator 端点路径。

  3. 会话策略: 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,ADMIN

生产环境安全加固建议:

  1. 使用强密码: Actuator 端点的密码应通过环境变量注入,不硬编码在配置文件中。
bash
# 通过环境变量设置密码
export ACTUATOR_PASSWORD=$(openssl rand -base64 32)
  1. 启用 HTTPS: Actuator 端点必须通过 HTTPS 访问,防止密码在网络中明文传输。

  2. 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. 审计 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();
        }
    }
}

自定义健康指标在健康检查中的表现:

json
{
  "status": "UP",
  "components": {
    "userCenter": {
      "status": "UP",
      "details": {
        "userCenter": "available",
        "responseTime": "15ms"
      }
    },
    "redis": {
      "status": "UP"
    }
  }
}

七、日志收集与分析

7.1 ELK Stack 集成

ELK Stack(Elasticsearch + Logstash + Kibana)是日志收集与分析领域最经典的解决方案。将 CAS 的三日志体系接入 ELK,可以实现集中化的日志管理、全文检索和可视化分析。

架构设计:

CAS Server → Filebeat → Logstash → Elasticsearch → Kibana

                    Redis/Kafka(缓冲)

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: 2048

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}"
  }
}

Kibana 可视化建议:

  1. 认证趋势仪表板: 展示每小时认证成功/失败数量趋势图。
  2. 认证耗时分布图: 展示认证耗时的 P50/P90/P99 分位数变化趋势。
  3. TOP 10 失败用户: 展示认证失败次数最多的用户列表。
  4. 服务访问热力图: 展示各服务的访问频率和时间分布。
  5. 实时告警面板: 展示最近的异常事件和告警信息。

7.2 Loki + Grafana

Loki + Grafana 是近年来兴起的轻量级日志收集方案,与 ELK 相比具有更低的资源消耗和更简单的运维成本。

架构设计:

CAS Server → Promtail → Loki → Grafana

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: RFC3339

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]))

Loki vs ELK 选型建议:

维度ELK StackLoki + Grafana
资源消耗高(Elasticsearch 内存密集)低(Loki 仅索引标签)
全文检索强大基本支持
运维复杂度
与监控集成需要额外配置原生集成
适用场景复杂日志分析日志+监控一体化
成本

7.3 阿里云日志服务

在国内云环境中,阿里云日志服务(SLS)是常用的日志收集方案。我们的实际项目中,CAS 5.3 的 build.gradlepom.xml 都引入了阿里云日志 SDK 依赖:

groovy
// CAS 5.3 Gradle 构建配置
dependencies {
    // 阿里云日志依赖
    implementation 'com.aliyun.openservices:aliyun-log:0.6.56'
}
xml
<!-- CAS 5.3 Maven 构建配置 -->
<dependency>
    <groupId>com.aliyun.openservices</groupId>
    <artifactId>aliyun-log</artifactId>
    <version>0.6.56</version>
</dependency>

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. 日志库分离: 为应用日志、审计日志、性能日志创建不同的 Logstore,便于独立设置保留策略和查询权限。
  2. 通过环境变量管理密钥: AccessKey ID 和 Secret 通过环境变量注入,不硬编码在配置文件中。
  3. 使用 RAM 角色: 在 ECS/K8s 环境中,推荐使用 ECS RAM 角色或 K8s OIDC 角色,避免使用静态 AccessKey。
  4. 设置合理的 Shard 数量: 根据日志量设置合适的 Shard 数量,通常每个 Shard 可以处理 5MB/s 的写入流量。

7.4 日志告警策略

日志告警是将被动日志分析转化为主动故障发现的关键机制。以下是针对 CAS 三日志体系的告警策略建议。

应用日志告警规则:

告警规则条件严重级别响应时间
CAS 启动失败CasWebApplication 出现 FATALP0立即
认证异常AUTHENTICATION_FAILED 5分钟内超过 100 次P115分钟
票据存储异常Redis 连接失败P0立即
内存溢出OutOfMemoryErrorP0立即
数据库连接异常Connection refusedConnection timeoutP115分钟

审计日志告警规则:

告警规则条件严重级别响应时间
暴力破解检测同一 IP 5分钟内认证失败超过 50 次P115分钟
异常时间登录非工作时间的管理员登录P21小时
异常地域登录非常用地区的登录请求P21小时
票据伪造嫌疑不存在的 ST/TGT 被频繁验证P0立即

性能日志告警规则:

告警规则条件严重级别响应时间
认证耗时飙升P95 认证耗时超过 500ms 持续 5 分钟P115分钟
票据创建耗时异常TGT 创建耗时超过 200msP21小时
Redis 响应缓慢Redis 操作耗时超过 100msP115分钟

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 > 100

八、生产环境日志最佳实践

8.1 日志级别建议

生产环境的日志级别配置需要在可观测性性能开销之间取得平衡。以下是我们在实际项目中总结的日志级别最佳实践。

核心原则:

  1. 默认保守,按需放开: 生产环境默认使用较高的日志级别(INFO/WARN),仅在排查特定问题时临时降低相关包的日志级别。
  2. 利用热更新能力: CAS 5.3/6.6 的 log4j2.xml 支持 monitorInterval="5" 的热更新,可以在不重启服务的情况下调整日志级别。
  3. 分级分类控制: 不同类型的日志使用不同的级别策略。

推荐日志级别配置矩阵:

包/组件开发环境测试环境生产环境说明
org.apereo.casDEBUGINFOINFOCAS 核心框架
org.apereo.cas.web.flowDEBUGDEBUGINFOWeb Flow 流程
org.apereo.inspektr.auditINFOINFOINFO审计日志
perfStatsLoggerINFOINFOINFO性能统计
org.springframeworkDEBUGINFOWARNSpring 框架
org.springframework.securityDEBUGINFOWARNSpring Security
org.springframework.webDEBUGINFOWARNSpring Web
org.apacheDEBUGWARNERRORApache 基础库
org.pac4jDEBUGWARNOFFPAC4J 认证库
org.opensamlDEBUGWARNOFFSAML 库
org.thymeleafDEBUGWARNOFF模板引擎
cc.bima.casDEBUGINFOINFO项目自定义代码

动态日志级别调整脚本:

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)"

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>
  • 文件大小: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>
  • 文件大小: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>
  • 文件大小:500MB
  • 保留天数:180天(满足等保2.0要求)
  • 最大文件数:365

8.3 磁盘空间管理

日志文件是磁盘空间消耗的主要来源,需要建立完善的磁盘空间管理机制。

磁盘空间预算规划:

假设 CAS 集群每天处理 100 万次认证请求:

日志类型单条大小日志量/天日增长量30天总量
应用日志200B500万行~1GB~30GB
审计日志500B100万行~500MB~15GB
性能日志150B100万行~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
fi

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
}

8.4 敏感信息脱敏

CAS 作为认证系统,日志中不可避免地会包含用户名、IP 地址等敏感信息。在生产环境中,必须对日志中的敏感信息进行脱敏处理。

CAS 内置的脱敏机制:

CAS 框架内置了基本的敏感信息脱敏能力,主要通过以下方式实现:

  1. 密码脱敏: CAS 在记录认证日志时,会自动将密码替换为 ******
  2. Ticket 脱敏: 票据 ID 在日志中通常只显示前缀部分。
  3. CasAppender 拦截: CasAppender 可以在日志输出前进行自定义的脱敏处理。

自定义脱敏配置示例:

log4j2.xml 中使用 Log4j2 的 Rewrite Policy 实现脱敏:

xml
<Rewrite name="sanitizeRewrite">
    <RewritePolicy>
        <!-- 脱敏规则 -->
        <PropertiesRewritePolicy>
            <Property name="password" value="******" />
            <Property name="credential" value="******" />
        </PropertiesRewritePolicy>
    </RewritePolicy>
</Rewrite>

自定义脱敏 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;
    }
}

脱敏规则建议:

敏感信息类型脱敏规则示例
密码完全替换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关联的票据 IDTGT-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" />

输出示例:

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.com

通过 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");
        }
    }
}

与 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'
}
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: otlp

总结与展望

本文从 CAS Overlay 项目的实际配置出发,系统性地解析了 CAS 监控与日志体系的完整架构。让我们回顾一下各版本的核心配置要点:

版本配置要点总结:

维度CAS 5.3CAS 6.6CAS 7.3
日志配置log4j2.xmllog4j2.xmlapplication.yml
异步日志AsyncLoggerAsyncLoggerSpring Boot 自动
三日志分离支持支持需自定义
Actuator 配置endpoints.enabledexposure.includeexposure.include
健康检查/status/health/status/health/status/health
安全角色ACTUATOR,ADMINACTUATOR,ADMINACTUATOR,ADMIN
日志热更新monitorInterval=5monitorInterval=5需 Spring Cloud
Java 版本1.81117+
Spring Boot1.x/2.x2.x3.x

关键配置文件清单:

文件版本用途
log4j2.xml5.3/6.6Log4j2 日志配置
application.yml5.3/6.6/7.3Spring Boot 应用配置
build.gradle5.3/6.6/7.3Gradle 构建配置(日志依赖)
pom.xml5.3/6.6/7.3Maven 构建配置(日志依赖)
log4j2-spring.xml7.3(可选)Log4j2 自定义配置

未来展望:

随着 CAS 版本的持续演进,日志和监控体系也在不断现代化。CAS 7.x 系列已经全面拥抱 Spring Boot 3.x 和 Jakarta EE,未来的版本将进一步深化与 OpenTelemetry、Micrometer 等可观测性标准的集成。对于企业级 CAS 部署,建议:

  1. 建立完善的日志规范: 制定统一的日志格式、级别、脱敏标准。
  2. 建设集中式日志平台: 基于 ELK 或 Loki+Grafana 建设统一的日志收集与分析平台。
  3. 实施主动监控告警: 基于日志和指标建立多层次的告警体系。
  4. 定期审计日志配置: 每季度审查日志级别、保留策略和安全配置。
  5. 关注版本演进: 及时跟进 CAS 新版本的可观测性特性,合理规划升级路径。

版权声明: 本文为必码(bima.cc)原创技术文章,仅供学习交流。

本文内容基于实际项目源码解析整理,代码示例均为教学简化版本,仅供学习参考。

文档内容提取自项目源码与配置文件,如需获取完整项目代码,请访问 bima.cc