大家好,我是月夜枫。
在微服务、多子系统、多后台项目开发中,经常会遇到多个系统需要统一登录的需求:用户只需要登录一次,所有关联子系统全部认证通过,无需重复输入账号密码,这种方案就是单点登录 SSO(Single Sign On)。目前企业内部系统、OA后台、多模块管理平台最主流成熟的方案就是Apereo CAS。
在实现单点登录(Single Sign-On, SSO)功能时,有多种技术方案可以选择,其中包括使用CAS(Central Authentication Service)服务。CAS是一种广泛使用的开源单点登录解决方案,它允许用户在一个系统中登录后,无需再次登录即可访问其他受保护的应用程序或服务。
一、CAS 单点登录核心概念
1. 什么是 CAS
CAS(Central Authentication Service)中心认证服务,是 Apereo 开源的企业级单点登录解决方案。
核心特点:统一认证中心,所有子系统统一跳转认证中心登录,登录成功后所有客户端共享会话,一处登录全部系统免登,一处全部登出。
2. CAS 架构组成
CAS Server 认证服务端
独立部署的统一登录中心,负责登录页面、账号密码校验、票据(Ticket)颁发、会话管理、统一登出。所有客户端全部依赖该服务端。CAS Client 客户端
我们自己开发的各个 SpringBoot 业务系统,不处理登录逻辑,所有认证全部交给 CAS 服务端,只负责接收、校验服务端发放的票据。
3. CAS 完整单点登录流程
用户访问客户端A业务系统,客户端无登录信息,自动重定向到CAS 统一认证中心
用户在认证中心输入账号密码完成登录
认证中心校验账号合法,生成全局唯一TGT(Ticket Granting Ticket)票据,存入服务端会话
认证中心携带ST(Service Ticket)服务票据重定向回到客户端A
客户端A携带ST去CAS服务端校验票据合法性
校验通过,客户端A建立本地会话,用户成功访问资源
用户再访问客户端B、客户端C,无需再次登录,直接重定向CAS,服务端识别已有TGT,直接发放新ST票据,客户端校验通过直接访问
统一登出:在任意系统退出,CAS 销毁全局 TGT 票据,所有客户端全部失效登出。
4. 三种单点登录方案场景对比
方案 | 适用场景 | 特点 |
CAS | 企业内部多系统、后台管理系统、内网平台 | 中心化强认证、统一登录页面、统一权限、原生登出联动 |
OAuth2.0 | 互联网第三方登录、外网应用、多平台授权 | 第三方授权、不统一账号体系、侧重授权而非认证 |
JWT无状态Token | 微服务前后端分离、无状态分布式接口 | 无状态、无中心认证、不依赖服务端会话 |
二、环境版本说明
SpringBoot 2.7.x(稳定兼容版,和前文项目版本统一)
Apereo CAS 6.6.x(稳定版,兼容性最好)
JDK 1.8+
多客户端测试:客户端1、客户端2 两个独立 SpringBoot 项目模拟多系统
端口规划
CAS 认证服务端:
8443(CAS官方默认HTTPS端口)客户端1:
8081客户端2:
8082
三、CAS 服务端搭建(统一认证中心)
CAS官方提供完整原生服务端模板,我们直接基于官方 WAR 包改造,无需从零开发,快速搭建统一登录中心。
1. 服务端部署方式
下载 Apereo CAS 官方 overlay 项目
基于 Maven 构建项目,修改登录账号、页面、配置
启动服务端,默认支持 HTTPS 8443 端口
内置简单账号验证,可后续对接数据库用户表
2. CAS 服务端核心配置修改
(1)修改配置文件cas.properties
修改默认账号密码、允许客户端访问地址、票据配置、关闭严格HTTPS校验(开发环境)。
# 服务端访问地址 cas.server.name=https://localhost:8443 cas.server.prefix=${cas.server.name}/cas # 开发环境关闭HTTPS强制校验(生产环境必须开启HTTPS) tgc.secure=false ticket.secure=false cookie.secure=false # 自定义登录账号密码(默认账号 casuser/Mellon) cas.authn.static.credentials[0].username=admin cas.authn.static.credentials[0].password=123456 # 允许接入的客户端服务地址 cas.serviceRegistry.json.location=classpath:/services(2)配置允许接入的客户端服务
在services目录配置 JSON 文件,放行两个客户端域名地址。
{ "@class" : "org.apereo.cas.services.RegexRegisteredService", "serviceId" : "^(http|https)://localhost:8081.*", "name" : "Client1-业务系统1", "id" : 1 }{ "@class" : "org.apereo.cas.services.RegexRegisteredService", "serviceId" : "^(http|https)://localhost:8082.*", "name" : "Client2-业务系统2", "id" : 2 }配置完成后启动 CAS 服务端,访问地址:https://localhost:8443/cas/login
出现 CAS 官方登录页面,输入配置账号admin/123456即可登录成功。
四、SpringBoot 客户端整合 CAS
下面开始客户端完整接入,所有代码格式、依赖、风格完全延续你之前所有SpringBoot文章,封装统一配置、拦截器、票据校验,多客户端通用代码。
1. 客户端统一引入依赖 pom.xml
所有业务客户端统一引入cas-client-autoconfig-supportSpringBoot 自动化整合依赖,开箱即用,无需手动手写原生复杂API。
<!-- SpringBoot Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- CAS 客户端自动化整合依赖 --> <dependency> <groupId>org.apereo.cas.client</groupId> <artifactId>cas-client-autoconfig-support</artifactId> <version>2.3.0-GA</version> </dependency> <!-- Lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>2. application.yml 配置文件
客户端1(8081端口)
server: port: 8081 # CAS 客户端配置 cas: # CAS统一认证服务端地址 cas-server-url: http://localhost:8443/cas # 当前客户端自身访问地址 service-url: http://localhost:8081 # 开启CAS自动配置 enabled: true # 单点登出 single-logout: true # 校验票据 validate-ticket: true客户端2(8082端口)
仅修改自身端口与服务地址,其余配置完全一致。
server: port: 8082 cas: cas-server-url: http://localhost:8443/cas service-url: http://localhost:8082 enabled: true single-logout: true validate-ticket: true3. 开启CAS客户端自动配置注解
在SpringBoot启动类添加@EnableCasClient注解,自动加载CAS拦截器、票据校验、重定向、单点登出全套功能。
import org.apereo.cas.client.boot.configuration.EnableCasClient; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @EnableCasClient // 开启CAS单点登录客户端自动配置 public class CasClient1Application { public static void main(String[] args) { SpringApplication.run(CasClient1Application.class, args); } }4. 统一返回结果类 Result
复用前文统一格式,保持全系列文章代码统一。
import lombok.Data; @Data public class Result<T> { private int code; private String msg; private T data; public static <T> Result<T> success(T data) { Result<T> result = new Result<>(); result.setCode(200); result.setMsg("操作成功"); result.setData(data); return result; } public static <T> Result<T> error(String msg) { Result<T> result = new Result<>(); result.setCode(500); result.setMsg(msg); return result; } }5. 获取登录用户信息工具类
CAS登录成功后,会把登录用户名存入 Request 域,封装工具类方便全局获取当前登录用户。
import javax.servlet.http.HttpServletRequest; public class CasUserUtil { /** * 从CAS认证上下文获取当前登录用户名 * @param request 请求对象 * @return 登录账号 */ public static String getLoginUsername(HttpServletRequest request){ return (String) request.getAttribute("_cas_username"); } }6. 测试接口
编写普通业务接口,无需任何登录代码,全部认证由CAS拦截器自动处理。
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; @RestController @RequestMapping("/client") public class ClientTestController { /** * 客户端1测试接口 */ @GetMapping("/info1") public Result<String> client1Info(HttpServletRequest request){ String username = CasUserUtil.getLoginUsername(request); return Result.success("【业务系统1】登录用户:" + username); } }客户端2同样编写对应测试接口/client/info2。
五、完整单点登录流程测试
首先启动CAS 8443端口认证服务端
依次启动客户端1(8081)、客户端2(8082)
- 直接访问客户端1接口:
无登录信息,自动重定向跳转到CAS统一登录中心http://localhost:8081/client/info1 在
https://localhost:8443/cas/login输入账号admin/123456完成登录登录成功自动重定向回到客户端1,正常返回用户信息
- 直接新窗口访问客户端2接口
无需任何登录操作,直接识别已登录凭证,直接返回登录用户信息,单点登录完成。http://localhost:8082/client/info2 - 统一单点登出:访问任意系统登出地址
CAS服务端销毁全局票据TGT,两个客户端全部同时失效,再次访问全部需要重新登录。http://localhost:8443/cas/logout
六、开发环境HTTPS问题解决方案
CAS官方默认强制HTTPS协议,本地开发会出现证书报错、无法访问问题,提供两种解决方案:
1.方案1:配置本地自签名证书
JDK生成密钥库证书,配置到CAS服务端Tomcat,完美兼容官方原生流程,生产环境标准方案。2.方案2:开发环境HTTP兼容改造(推荐本地测试)
修改CAS服务端配置,关闭SSL强制校验,支持HTTP协议访问,无需证书,本地开发调试便捷。
# 关闭SSL cas.tls.enable=false七、扩展:CAS对接数据库真实用户表
原生CAS仅支持静态硬编码账号,实际项目需要对接MySQL数据库账号密码登录,只需在CAS服务端新增数据库认证配置,引入JDBC依赖,配置数据库连接即可。
# CAS服务端数据库认证配置 cas.authn.jdbc.query[0].url=jdbc:mysql://localhost:3306/cas_db cas.authn.jdbc.query[0].user=root cas.authn.jdbc.query[0].password=root cas.authn.jdbc.query[0].sql=select password from sys_user where username=? cas.authn.jdbc.query[0].password-encoder-type=BCRYPT实现企业真实账号体系、密码加密校验、数据库用户统一登录。
八、CAS 结合 JWT 改造
原生CAS基于Session会话,微服务分布式场景不适用,可进行改造:
CAS服务端登录成功后,不发放传统票据,签发JWT令牌
各个客户端携带JWT访问接口
统一JWT鉴权中心,实现无状态分布式单点登录
兼顾CAS统一登录中心 + JWT无状态微服务优势。
九、常见问题与踩坑总结
客户端无法重定向CAS服务端
检查yml中服务端地址、客户端自身地址配置是否正确,端口是否对应。ST票据校验失败
服务端未配置客户端服务白名单,serviceId正则不匹配,未放行客户端地址。跨域、重定向地址不匹配
客户端service-url必须和CAS服务端注册的地址完全一致。单点登出不生效
客户端配置single-logout: true未开启,TGT全局票据未销毁。HTTPS证书不安全报错
本地开发关闭SSL校验,生产环境必须配置正规域名证书。
十、全系列方案总结对比
结合前面3篇文章做完整体系总结:
102 JWT无状态认证:前后端分离、微服务接口无状态鉴权,无中心。
103 OAuth2.0第三方登录:互联网项目第三方账号授权登录,开放授权场景。
104 CAS单点登录SSO:企业内部多系统统一登录,中心化认证,一处登录全部免登。
项目选型建议
多后台内部系统、OA、统一管理平台 →CAS
外网APP、网站第三方登录 →OAuth2.0
纯前后端分离接口、微服务权限体系 →JWT
学习本就是一个长期积累的过程,没有捷径,唯有坚持。希望能够真正帮到你,学以致用,不断提升,在自己的领域里越走越远。
互动时刻:你在项目遇到过什么有趣的问题?欢迎在留言区分享你的经验!👇如果这篇文章对你有帮助,别忘了点赞、在看、转发三连支持!每天分享更多硬核技术干货!💪