什么是单点登录

单点登录(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.comapp2.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的共享存储和安全策略都是必须认真设计的关键环节。