CAS 5.3 单点登录(SSO)从零部署与核心配置实战指南
1. 项目概述:为什么是CAS 5.3?
在分布式系统和微服务架构成为主流的今天,身份认证与授权(Authentication & Authorization)是每个开发者绕不开的核心议题。单点登录(SSO)作为解决多系统间用户身份统一认证的成熟方案,其重要性不言而喻。而当我们谈论开源的单点登录解决方案时,Apereo CAS(Central Authentication Service)几乎是绕不开的名字。它历史悠久、功能强大、社区活跃,是企业级SSO的标杆之一。
我选择CAS 5.3.x这个版本作为切入点,是因为它是一个承上启下的关键版本。CAS 5.x系列相较于早期的3.x、4.x,在架构上进行了彻底的重构,拥抱了Spring Boot,使得部署和定制化变得前所未有的简单。而5.3.x版本,在5.x系列中已经相当成熟和稳定,修复了大量早期5.x版本的Bug,同时引入了许多对现代开发友好的特性,比如对OAuth 2.0、OpenID Connect更完善的支持,以及更灵活的属性管理和服务注册表配置。对于大多数从零开始构建SSO体系,或者从老旧版本升级的团队来说,5.3.x是一个风险可控、功能完备的绝佳起点。
简单来说,部署和使用CAS 5.3,就是为你的一系列应用(无论是Java EE的老系统,还是Spring Cloud的新服务)建立一个统一、安全、可扩展的“门户保安”。用户只需登录一次,即可畅通无阻地访问所有接入的应用,极大地提升了用户体验和系统安全性。接下来,我将从零开始,带你完成一次完整的CAS 5.3部署,并深入其核心配置与使用。
2. 部署环境准备与构建选择
在动手敲命令之前,理清部署思路和选择合适的构建方式至关重要。CAS 5.3提供了多种部署形态,我们需要根据自身的技术栈和运维习惯做出选择。
2.1 核心依赖与环境清单
无论选择哪种方式,以下基础环境是必须的:
- Java环境:CAS 5.3.x官方推荐使用JDK 8或JDK 11。我个人强烈建议使用JDK 11(LTS版本),它在性能、内存管理和后续兼容性上表现更好。确保
JAVA_HOME环境变量正确配置。 - 构建工具:CAS官方主推基于Gradle的Overlay构建方式。这也是本文重点介绍的方式。你需要安装Gradle(版本兼容即可,如6.x, 7.x),或者直接使用Gradle Wrapper(项目自带)。
- 版本控制:强烈建议使用Git来管理你的定制化配置。
- 操作系统:Linux(如CentOS 7+/Ubuntu 18.04+)是生产环境首选。Windows或macOS可用于开发测试。
2.2 构建方式选型:为什么推荐Overlay?
CAS项目本身是一个庞大的、高度可配置的Web应用。官方不建议直接修改其庞大的源代码库。取而代之的是“Overlay”模式。你可以把它理解为一个“模板项目”或“基础镜像”。
- 原理:你从一个官方的、空白的CAS Overlay模板项目开始。这个模板预定义了所有依赖和基础结构。你的所有定制——无论是修改配置文件、添加第三方依赖(如数据库驱动、Redis连接器),还是替换UI页面——都只发生在你这个Overlay项目中。
- 好处:
- 关注点分离:你的代码只包含定制部分,与CAS核心代码完全隔离。
- 易于升级:升级CAS版本时,你只需更新Overlay模板中定义的CAS核心版本号,然后重新构建即可。你的定制化配置大部分可以保留。
- 清晰明了:项目结构干净,所有自定义内容一目了然。
因此,我们接下来的所有步骤,都将基于Gradle Overlay项目展开。
注意:网络上可能还存在基于Maven或直接下载WAR包的部署教程。对于CAS 5.x,Overlay是官方最佳实践,能避免大量路径和依赖冲突问题,请务必遵循。
3. 基于Gradle Overlay的详细部署流程
现在,我们进入实战环节。假设我们的工作目录是/opt/cas。
3.1 获取并初始化Overlay项目
首先,从CAS官方仓库获取Overlay模板。这里我们使用5.3.x系列的一个具体版本,例如5.3.16。
# 进入工作目录 cd /opt # 使用官方提供的初始化脚本(推荐) # 这会下载对应版本的模板并解压 wget https://github.com/apereo/cas-overlay-template/archive/refs/tags/v5.3.16.zip unzip v5.3.16.zip mv cas-overlay-template-5.3.16 cas cd cas # 或者,你也可以直接克隆整个模板仓库,然后切换到对应tag # git clone https://github.com/apereo/cas-overlay-template.git cas # cd cas # git checkout 5.3.16执行完毕后,你会看到一个标准的Gradle项目目录结构,关键文件如下:
build.gradle:项目构建文件,定义依赖和插件。src/main/resources/:配置文件目录(初始可能是空的或有一些示例)。src/main/webapp/:Web静态资源目录(如CSS, JS, 图片)。gradlew和gradlew.bat:Gradle包装器脚本,用于统一构建环境。
3.2 核心配置详解:application.yml与cas.properties
CAS的配置是重中之重。5.x版本支持多种配置格式,推荐使用application.yml(层次清晰)或application.properties。我们以application.yml为例,在src/main/resources目录下创建它。
一个最小化但可运行的配置,需要定义服务器端口、SSL(HTTPS)以及一个静态的服务注册列表(用于测试)。
# src/main/resources/application.yml server: port: 8443 ssl: enabled: true key-store: file:/etc/cas/thekeystore key-store-password: changeit key-password: changeit cas: server: name: https://cas.example.org:8443 serviceRegistry: initFromJson: true logging: level: org.apereo.cas: INFO配置解析与实操要点:
SSL配置(HTTPS):CAS强制要求HTTPS,这是安全的基础。你需要一个Keystore文件。
- 生成测试Keystore(仅用于开发/测试):
执行命令后,会交互式地询问一些信息(名字与姓氏最重要,应输入你访问CAS的域名或IP,如keytool -genkey -alias cas -keyalg RSA -keysize 2048 -keystore /etc/cas/thekeystore -validity 3650cas.example.org或localhost),最后设置密码(如changeit)。请确保application.yml中的路径和密码与此一致。 - 生产环境:必须使用由可信CA(如Let‘s Encrypt)签发的正式证书。可以将证书导入到Keystore中,或直接配置
server.ssl.key-store指向你的证书文件。
- 生成测试Keystore(仅用于开发/测试):
cas.server.name:这是CAS服务器对外提供服务的基准URL,必须与用户浏览器访问的地址一致,且必须是HTTPS。所有重定向和票据生成都基于此URL。服务注册表:
initFromJson: true告诉CAS从JSON文件加载注册的服务(即允许使用CAS登录的应用)。我们接下来创建这个文件。
3.3 注册第一个应用(服务)
在src/main/resources/services目录下,创建一个JSON文件,例如MyWebApp-10000001.json。文件名格式有要求,通常以-数字ID.json结尾。
{ "@class": "org.apereo.cas.services.RegexRegisteredService", "serviceId": "^(https|http)://app1.example.org/.*", "name": "MyWebApp", "id": 10000001, "description": "这是我的第一个接入CAS的应用", "evaluationOrder": 1, "logoutType": "BACK_CHANNEL", "attributeReleasePolicy": { "@class": "org.apereo.cas.services.ReturnAllowedAttributeReleasePolicy", "allowedAttributes": ["email", "displayName"] } }配置解析:
@class:指定服务注册的实现类,RegexRegisteredService表示使用正则表达式匹配服务URL。serviceId:一个正则表达式,匹配允许使用此CAS服务的应用URL。例如^(https|http)://app1.example.org/.*匹配所有以app1.example.org开头的HTTP/HTTPS请求。这是安全的关键,必须精确控制。id:服务的唯一数字ID,不能重复。evaluationOrder:评估顺序,数字越小优先级越高。logoutType:登出类型,BACK_CHANNEL表示CAS服务器会主动向后端服务发送登出请求(单点登出)。attributeReleasePolicy:属性释放策略,定义CAS在认证成功后,可以传递哪些用户属性(如邮箱、显示名)给该服务。这实现了基础的授权信息传递。
3.4 构建与运行
配置完成后,就可以构建并运行CAS了。
# 在项目根目录 (/opt/cas) 下执行 # 方式一:使用Gradle Wrapper进行构建并生成可执行WAR ./gradlew clean build # 构建成功后,会在 `build/libs/` 目录下生成一个 `cas.war` 文件。 # 方式二:直接使用Gradle BootRun任务运行(适合开发调试) ./gradlew bootRun如果使用bootRun,控制台会输出日志,启动成功后,你可以通过https://localhost:8443/cas/login访问CAS的登录页面。默认用户名/密码是casuser/Mellon。
第一次登录实操心得:启动后访问登录页,可能会遇到SSL证书警告(因为用的是自签名证书),在浏览器中选择“继续前往”或“接受风险”即可。成功登录后,你会看到一个简单的CAS欢迎页面,上面会显示你的登录状态和一些基本信息。这证明CAS服务器本身已经成功运行。
4. 深入核心功能配置与集成
一个基础的CAS服务器跑起来了,但这远远不够。接下来我们要让它变得有用,即与各种存储后端和认证源集成。
4.1 认证源集成:从静态用户到数据库
默认的静态用户(casuser/Mellon)仅用于测试。生产环境必须连接真实的用户存储。
4.1.1 集成JDBC(MySQL/PostgreSQL)
假设我们使用MySQL。首先,在build.gradle的dependencies部分添加JDBC和MySQL驱动依赖:
dependencies { // 其他依赖... implementation "org.apereo.cas:cas-server-support-jdbc:${project.'cas.version'}" implementation "mysql:mysql-connector-java:8.0.33" }然后,在application.yml中添加数据源和查询配置:
cas: authn: jdbc: query: - driver-class: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/your_auth_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC user: db_user password: db_password field-password: password # 数据库表中密码字段名 field-expired: expired field-disabled: disabled sql: SELECT * FROM users WHERE username = ? password-encoder: type: DEFAULT # 或 BCRYPT, SSHA 等,需与数据库中存储的密码格式匹配 encoding-algorithm: MD5 # 如果密码是MD5哈希存储 character-encoding: UTF-8关键点解析:
sql:根据用户名查询用户的SQL语句。CAS会使用登录时输入的用户名替换?。password-encoder:这是最容易出错的地方!必须与数据库中密码的存储方式一致。如果数据库存的是明文,则不需要编码器。如果是MD5哈希,就配置type: DEFAULT和encoding-algorithm: MD5。现代系统推荐使用BCRYPT。field-expired和field-disabled:对应数据库中表示账户是否过期、禁用的字段名(可选),CAS会据此拒绝登录。
4.1.2 集成LDAP(Active Directory/OpenLDAP)
对于使用AD域控的企业,集成LDAP是更常见的选择。
在build.gradle中添加依赖:
implementation "org.apereo.cas:cas-server-support-ldap:${project.'cas.version'}"在application.yml中配置:
cas: authn: ldap: - type: AUTHENTICATED # 或 DIRECT, AD 等 ldap-url: ldap://ad.example.org:389 base-dn: dc=example,dc=org search-filter: sAMAccountName={user} bind-dn: cn=binduser,ou=ServiceAccounts,dc=example,dc=org bind-credential: bindpassword principal-attribute-list: sAMAccountName,mail,displayName配置解析:
type:AUTHENTICATED表示CAS先用bind-dn凭证绑定LDAP,再搜索用户;AD是Active Directory的优化类型。search-filter:搜索用户的过滤器,{user}会被替换为登录用户名。bind-dn和bind-credential:一个有权限在LDAP中搜索用户的账户。principal-attribute-list:认证成功后,从LDAP条目中获取哪些属性,这些属性可以释放给服务。
4.2 票据存储:从内存到Redis集群
默认情况下,CAS生成的Ticket(如TGT-票据授予票据,ST-服务票据)存储在内存中。这意味着服务器重启后所有登录状态丢失,且无法在集群环境下共享。生产环境必须使用外部存储。
集成Redis作为Ticket Registry:
添加依赖:
implementation "org.apereo.cas:cas-server-support-redis-ticket-registry:${project.'cas.version'}"配置application.yml:
cas: ticket: registry: redis: host: localhost port: 6379 password: your_redis_password # 如果没有密码则省略 database: 0 timeout: 5000这样,所有票据都将持久化到Redis中。即使CAS服务器实例重启或扩容,用户的登录状态也不会丢失,完美支持集群部署。
4.3 管理界面与监控
CAS提供了一个内置的管理端点(Actuator Endpoints),默认不开启。开启后可以查看健康状态、指标、环境配置等。
在application.yml中添加:
management: endpoints: web: exposure: include: health,info,metrics,env endpoint: health: show-details: always然后,你可以通过https://cas.example.org:8443/cas/actuator/health来检查服务健康状态。为了安全,生产环境应通过Spring Security限制对这些端点的访问。
5. 客户端应用集成示例
服务端配置好了,客户端(你的业务应用)如何接入?这里以两个最常见的场景为例。
5.1 Java Web应用集成(使用官方CAS Client)
对于传统的Servlet-based应用(如Spring MVC, J2EE),可以使用cas-client-core。
添加Maven依赖:
<dependency> <groupId>org.apereo.cas</groupId> <artifactId>cas-client-core</artifactId> <version>3.6.4</version> <!-- 注意选择与CAS服务端兼容的版本 --> </dependency>配置
web.xml(或通过Java Config):<!-- 认证过滤器 --> <filter> <filter-name>CAS Authentication Filter</filter-name> <filter-class>org.apereo.cas.client.authentication.AuthenticationFilter</filter-class> <init-param> <param-name>casServerLoginUrl</param-name> <param-value>https://cas.example.org:8443/cas/login</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>https://app1.example.org</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Authentication Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 票据验证过滤器 --> <filter> <filter-name>CAS Validation Filter</filter-name> <filter-class>org.apereo.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class> <init-param> <param-name>casServerUrlPrefix</param-name> <param-value>https://cas.example.org:8443/cas</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>https://app1.example.org</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Validation Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>获取用户信息:验证通过后,用户身份会存储在
HttpServletRequest的principal中,可以通过request.getUserPrincipal().getName()获取用户名。
5.2 Spring Boot应用集成(使用Spring Security)
对于现代Spring Boot应用,集成更加优雅。Spring Security提供了对CAS的原生支持。
添加依赖:
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-cas</artifactId> </dependency>配置SecurityConfig:
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Value("${cas.server.url}") private String casServerUrl; @Value("${app.server.url}") private String appServerUrl; @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .csrf().disable() .exceptionHandling() .authenticationEntryPoint(casAuthenticationEntryPoint()) .and() .addFilterBefore(casAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); } @Bean public CasAuthenticationEntryPoint casAuthenticationEntryPoint() { CasAuthenticationEntryPoint entryPoint = new CasAuthenticationEntryPoint(); entryPoint.setLoginUrl(casServerUrl + "/login"); entryPoint.setServiceProperties(serviceProperties()); return entryPoint; } @Bean public ServiceProperties serviceProperties() { ServiceProperties sp = new ServiceProperties(); sp.setService(appServerUrl + "/login/cas"); sp.setSendRenew(false); return sp; } @Bean public CasAuthenticationFilter casAuthenticationFilter() throws Exception { CasAuthenticationFilter filter = new CasAuthenticationFilter(); filter.setAuthenticationManager(authenticationManager()); filter.setFilterProcessesUrl("/login/cas"); return filter; } @Bean public CasAuthenticationProvider casAuthenticationProvider() { CasAuthenticationProvider provider = new CasAuthenticationProvider(); provider.setAuthenticationUserDetailsService(userDetailsService()); provider.setServiceProperties(serviceProperties()); provider.setTicketValidator(cas20ServiceTicketValidator()); provider.setKey("CAS_PROVIDER_KEY"); return provider; } @Bean public Cas20ServiceTicketValidator cas20ServiceTicketValidator() { return new Cas20ServiceTicketValidator(casServerUrl); } // 需要实现 UserDetailsService,用于根据CAS返回的用户名加载用户权限等信息 @Bean public UserDetailsService userDetailsService() { // ... 返回你的UserDetailsService实现 } }
这样,当用户访问受保护的Spring Boot应用时,会被重定向到CAS登录,登录成功后返回应用,Spring Security会自动完成票据验证和会话建立。
6. 部署上线、运维与问题排查
将开发调试好的CAS部署到生产环境,还需要考虑一些运维层面的问题。
6.1 打包与部署
使用./gradlew clean build生成的cas.war是一个可执行的Fat Jar(内嵌了Tomcat)。部署方式非常灵活:
- 直接运行:
java -jar cas.war或java -Xmx1024m -jar cas.war。你可以使用nohup或systemd来守护进程。 - 部署到外部容器:虽然可以,但不推荐。因为Overlay项目默认打包的是可执行Jar,若要部署到独立的Tomcat,需要修改打包配置(
apply plugin: ‘war’),并处理内嵌容器冲突,复杂度较高。直接运行Jar是官方推荐方式。
6.2 日志与监控
- 日志配置:CAS使用Spring Boot的Logback。你可以在
src/main/resources/logback.xml中自定义日志格式、输出级别和文件滚动策略。生产环境建议将org.apereo.cas的日志级别设为WARN或ERROR,避免日志量过大。 - 健康检查:如前所述,利用Actuator的
/health端点,可以方便地集成到Kubernetes的Liveness/Readiness Probe或各类监控系统中。
6.3 常见问题与排查技巧实录
在部署和使用过程中,你几乎一定会遇到下面这些问题。这里我记录下最典型的几个及其解决思路。
问题1:登录成功,但重定向回应用时出现“无效票据(Invalid Ticket)”错误。
- 排查思路:这是最常见的问题,根本原因是CAS服务器生成的ST(服务票据)与应用验证时的不匹配或已过期。
- 检查时钟同步:确保CAS服务器和客户端应用服务器的系统时间完全同步(使用NTP)。票据的有效性严重依赖于时间戳。
- 检查服务注册:确认客户端应用的URL(
service参数)是否精确匹配在CAS服务端services/目录下JSON文件中定义的serviceId正则表达式。多一个斜杠或少一个端口号都可能不匹配。 - 检查网络连通性:确保客户端应用能通过网络访问CAS服务器的
https://cas.example.org:8443/cas地址(特别是/validate或/p3/serviceValidate端点)。 - 查看CAS服务器日志:日志中会记录票据的生成和验证过程。搜索票据ID,看是否有验证失败的记录,错误信息通常很明确。
问题2:集成LDAP/AD认证失败,提示“用户名密码错误”或“无法找到用户”。
- 排查思路:
- 测试LDAP连接:先用
ldapsearch或Apache Directory Studio等工具,使用配置中的bind-dn和bind-credential,手动执行search-filter对应的查询,看能否返回用户条目。这是验证LDAP配置是否正确的最直接方法。 - 检查过滤器语法:确保
search-filter中的属性名(如sAMAccountName,uid)在你的LDAP架构中存在且正确。 - 检查Base DN:
base-dn是否设置得过大或过小?确保目标用户在这个Base DN之下。 - 查看CAS DEBUG日志:将
org.apereo.cas日志级别临时调整为DEBUG,可以看到详细的LDAP绑定和搜索过程,有助于定位问题。
- 测试LDAP连接:先用
问题3:CAS管理界面(Actuator端点)或服务注册JSON文件修改后不生效。
- 排查思路:
- 服务注册缓存:CAS默认会缓存服务注册信息以提高性能。修改JSON文件后,需要触发重新加载。可以发送一个POST请求到
https://cas.example.org:8443/cas/actuator/serviceRegistry,或者直接重启CAS服务。 - 配置热加载:对于
application.yml,Spring Boot支持部分属性的热加载,但并非全部。最可靠的方式还是重启应用。对于生产环境,建议将配置外部化(如使用Spring Cloud Config),以实现真正的动态刷新。
- 服务注册缓存:CAS默认会缓存服务注册信息以提高性能。修改JSON文件后,需要触发重新加载。可以发送一个POST请求到
问题4:性能问题,登录或验证响应慢。
- 排查思路:
- 票据存储:如果使用默认的
内存存储,在用户量或票据量大时,清理过期票据的线程可能会造成停顿。务必切换到外部存储如Redis。 - 认证源:如果LDAP或数据库查询慢,会直接影响登录速度。检查认证源的网络延迟和查询性能,考虑增加连接池、优化查询语句或索引。
- 日志级别:生产环境务必把日志级别从
DEBUG/INFO调高到WARN,大量日志IO会消耗性能。 - JVM参数:确保为JVM分配了足够且合理的堆内存(
-Xms和-Xmx),并启用GC日志进行监控。
- 票据存储:如果使用默认的
部署CAS 5.3就像搭建一个核心枢纽,初期配置会有些繁琐,但一旦打通,对于整合企业内部纷杂的系统、提升安全性和用户体验的价值是巨大的。我的建议是,先在测试环境按照本文的步骤,从静态用户开始,逐步集成数据库、LDAP和Redis,把整个流程跑通,理解每个配置项的含义。然后再规划生产环境的部署架构,考虑高可用、负载均衡和监控告警。记住,仔细阅读官方文档和日志,它们是你解决问题的最佳伙伴。
