单点登录通过中心认证服务实现跨域身份验证,用户在SSO Server登录后获取一次性ticket,经重定向传递至客户端,客户端通过验证接口校验ticket并建立本地会话,利用Redis存储ticket信息并设置过期时间,结合HTTPS、来源校验等机制保障安全,登出时由中心通知各客户端销毁会话。
单点登录(SSO)的核心是让用户在一个系统中登录后,访问其他关联的系统时无需重复登录。在 PHP 中实现跨域名的 SSO,关键在于如何安全地共享用户身份凭证(ticket),尤其是在不同域名之间无法直接共享 Cookie 的情况下。
基本原理:基于中心认证服务的 Ticket 验证
搭建一个独立的认证中心(SSO Server),所有子系统(Client A、Client B 等)都信任该中心。用户首次访问任意子系统时,被重定向到 SSO Server 登录。登录成功后,SSO Server 发放一个一次性 ticket,并通过回调方式通知原系统完成身份确认。
典型流程如下:
- 用户访问 client-a.com,未登录,跳转至 sso-server.com?redirect=client-a.com/callback
- 用户在 sso-server.com 输入账号密码登录
- 登录成功,sso-server.com 生成唯一 ticket(如 tkt-abc123),存储在服务端(Redis 推荐)并设置有效期
- 重定向回 client-a.com/callback?ticket=tkt-abc123
- client-a.com 收到 ticket,向 sso-server.com/api/validate 发起请求验证 ticket 合法性
- sso-server 验证通过后返回用户信息,并标记该 ticket 已使用(防止重放攻击)
- client-a.com 创建本地会话,用户登录成功
跨域名共享的关键:Ticket 传递与验证
由于 Cookie 不可跨域共享(如 client-a.com 无法读取 client-b.com 的 session),必须依赖中心服务器进行身份仲裁。ticket 是一次性的,通过 URL 参数传递,安全性较高。
实现要点:
-
Ticket 存储: 使用 Redis 存储 ticket 与用户信息的映射,设置自动过期时间(如 5 分钟)
-
验证接口: SSO Server 提供 /api/validate?t=xxx 接口,返回 JSON 格式数据(是否有效、用户 ID、用户名等)
-
HTTPS 必须启用: 所有通信需走 HTTPS,防止 ticket 被窃听
-
Referer 或签名校验: 可增加来源域名白名单或请求签名,防止伪造验证请求
PHP 实现示例片段
SSO Server 生成 ticket(登录成功后):
$ticket = 'tkt_' . bin2hex(random_bytes(16));
$userId = 123;
// 存入 Redis,5分钟过期
$redis->setex($ticket, 300, json_encode(['user_id' => $userId, 'used' => false]));
header('Location: ' . $_GET['redirect'] . '?ticket=' . $ticket);
Client 端验证 ticket:
if (isset($_GE
T['ticket'])) {
$ticket = $_GET['ticket'];
$response = file_get_contents("https://sso-server.com/api/validate?t={$ticket}");
$data = json_decode($response, true);
if ($data['valid'] && !$data['used']) {
$_SESSION['user_id'] = $data['user_id'];
// 标记 ticket 已使用(由 server 完成)
}
}
SSO Server 验证接口(/api/validate):
$t = $_GET['t'];
$data = $redis->get($t);
if (!$data) {
echo json_encode(['valid' => false]); exit;
}
$info = json_decode($data, true);
if ($info['used']) {
echo json_encode(['valid' => false]); exit;
}
// 标记为已使用,防止重放
$info['used'] = true;
$redis->setex($t, 300, json_encode($info)); // 更新状态
echo json_encode(['valid' => true, 'user_id' => $info['user_id']]);
登出处理
登出需要通知所有已登录的客户端。可在 SSO Server 记录用户的 active sessions,登出时向各 client 发起异步登出请求(如 client-a.com/sso/logout?token=xxx),各 client 销毁本地 session。
基本上就这些。核心是信任中心 + 一次性 ticket + 安全传输。不复杂但容易忽略细节,比如 ticket 过期、重放、来源校验等。