单点登录
什么是单点登录
单点登录(Single Sign-On,简称 SSO)是一种身份认证方案,允许用户在一次登录后,访问多个相互信任的应用系统,而不需要为每个系统重复输入凭证。
举个生活中的例子:你登录了Google账号之后,可以直接使用Gmail、YouTube、Google Drive等服务,而不需要每次都重新登录。这背后就是SSO在起作用。
为什么要用SSO
在没有SSO的企业环境中,每个系统都有独立的登录页面和用户体系:
- 运维平台一套账号密码
- 监控平台另一套账号密码
- GitLab 又一套账号密码
用户需要记住多套凭证,每切换一个系统就要重新登录,体验很差。从管理角度看,每个系统独立维护用户数据,要修改密码或禁用某个员工账号时,需要在所有系统上逐个操作。
SSO的核心价值就是:一次认证,全网通行;一处注销,处处失效。
CAS协议
CAS(Central Authentication Service)是最经典的SSO实现协议。它的核心是引入了一个独立的认证中心,所有应用系统都把用户认证委托给它。
CAS完整流程
sequenceDiagram
participant User as 用户
participant Browser as 浏览器
participant App1 as 应用A
participant App2 as 应用B
participant CAS as CAS认证中心
User->>Browser: 访问应用A
Browser->>App1: GET /resource
App1->>Browser: 302 重定向到CAS (携带service参数)
Browser->>CAS: GET /login?service=http://app1/resource
CAS->>Browser: 返回登录页面
User->>Browser: 输入用户名密码
Browser->>CAS: POST 提交凭证
CAS->>CAS: 验证凭证,创建全局会话
CAS->>Browser: 302 重定向回应用A (携带ticket)
Browser->>App1: GET /resource?ticket=ST-xxx
App1->>CAS: 后台验证 ticket (HTTPS请求)
CAS->>App1: 验证成功,返回用户信息
App1->>App1: 创建本地会话
App1->>Browser: 返回资源内容(带cookie)
User->>Browser: 访问应用B
Browser->>App2: GET /resource
App2->>Browser: 302 重定向到CAS
Browser->>CAS: GET /login?service=http://app2/resource
Note over Browser,CAS: 浏览器携带了CAS域的cookie (已有全局会话)
CAS->>CAS: 校验全局会话有效
CAS->>Browser: 302 重定向回应用B (携带ticket)
Browser->>App2: GET /resource?ticket=ST-yyy
App2->>CAS: 后台验证ticket
CAS->>App2: 验证成功
App2->>Browser: 返回资源内容
关键角色
- 用户:携带浏览器访问各应用
- CAS Server:统一的认证中心,负责登录验证和票据管理
- CAS Client:各应用系统,集成CAS客户端,保护资源并处理票据验证
- TGT(Ticket Granting Ticket):用户登录成功后,CAS发给用户的票据,存储在CAS域的session 中
- ST(Service Ticket):用户访问某个具体服务时,CAS颁发的临时票据,一次性使用,有有效期
两个重要的细节
1. 为什么需要后台验证ticket
ticket是CAS通过URL参数传递给应用的,而URL参数可能被截获。如果应用直接信任URL中的ticket,就有安全风险。正确做法是应用在后台用HTTPS请求向CAS验证ticket的有效性,防止重放攻击。
2. 全局会话与本地会话
用户登录后,有两个会话同时存在:
- 全局会话(CAS域):存储TGT,标识用户在一次SSO周期中的认证状态
- 本地会话(应用域):各个应用自己维护的session,标识用户在该应用中的登录状态
用户登出时,需要同时销毁全局会话和所有应用的本地会话。这就是单点登出(Single Sign-Out)要做的事。
其他SSO方案
基于Cookie的共享方案
在同一个顶级域名下(如 .example.com),可以通过设置Cookie的domain来实现cookie共享。
1 | Set-Cookie: session_id=xxx; Domain=.example.com; Path=/ |
这种方案的缺点是显而易见的:受限于域名。如果应用部署在不同的顶级域名下(app1.com 和 app2.com),cookie无法跨域共享。
JWT + OAuth2.0
近年来更流行的方案是用JWT作为Token格式,配合OAuth2.0的授权流程来实现SSO。
flowchart LR
subgraph 认证过程
A[用户登录] --> B[认证服务签发JWT]
B --> C[JWT包含: 用户id/角色/过期时间/签名]
C --> D[应用验证JWT签名即可
无需每次都请求认证服务]
end
JWT方案相比CAS的好处是不需要额外的后台票据验证——应用只需要用公钥验证JWT签名的有效性,就能确认用户身份,减少了网络开销。
但是JWT也有缺点:签发之后无法主动失效(除非维护黑名单),所以在Token过期时间的设置上需要权衡。
方案对比
| 特性 | CAS | Cookie共享 | JWT+OAuth2 |
|---|---|---|---|
| 跨域名 | 支持 | 不支持 | 支持 |
| 实现复杂度 | 中等 | 简单 | 较高 |
| 安全性 | 高(HTTPS+一次性ticket) | 中 | 高 |
| 登出 | 较复杂 | 简单 | 较复杂 |
| 常见场景 | 企业内部系统 | 同域子系统 | 开放平台/移动端 |
实际应用中的考量
Session 存储
无论采用哪种方案,认证服务都需要存储会话信息。生产环境一般不会把session存在单机内存中,因为一旦服务重启,所有用户都会被强制下线。常见的做法是使用Redis作为 session的共享存储。
flowchart LR
subgraph 认证集群[认证服务集群]
S1[认证服务1]
S2[认证服务2]
end
Redis[(Redis Session共享)]
App1[应用A]
App2[应用B]
S1 --> Redis
S2 --> Redis
App1 -- 验证凭证 --> 认证集群
App2 -- 验证凭证 --> 认证集群
安全问题
SSO是身份认证的关键节点,安全上稍有不慎就是严重问题:
- HTTPS是必须的:认证过程中涉及密码、ticket、Token等敏感信息,必须全程加密传输
- ticket一次性:ST使用一次后立即失效,防止重放攻击
- 适当的过期时间:全局会话不能太长也不能太短,通常根据安全等级设置在4~8小时
- 登出机制:不仅要清除CAS域的全局会话,还要通知所有应用清除本地会话
总结
SSO解决了多系统间的认证统一问题,它的本质是将认证逻辑从各个应用中抽离出来,交给一个独立的认证中心统一处理。CAS协议作为最经典的实现方案,通过TGT和ST双重票据机制,在保证安全性的前提下实现了平滑的单点登录体验。
在实际选型中,如果是企业内部系统,CAS是成熟稳定的选择;如果是面向互联网开放平台,JWT + OAuth2.0更灵活,也更适合与第三方系统集成。无论哪种方案,session的共享存储和安全策略都是必须认真设计的关键环节。
文章作者:米兰
原始链接:https://blog.milanchen.site/posts/sso.html
版权声明:转载请声明出处