一份面向零基础读者的技术文档。读完后你将完全理解为什么在淘宝登录后打开天猫发现自己也登录了。目录现象描述前置知识三个必须理解的概念全景概览一句话说清原理完整流程六个阶段逐帧拆解深入理解你可能还会问的问题安全机制为什么这套方案不会被攻破现代浏览器的挑战与应对动手验证自己看一遍整个过程总结1. 现象描述你打开浏览器依次做了以下操作访问https://www.taobao.com/页面显示未登录点击登录跳转到https://login.taobao.com/输入账号密码登录成功自动跳回淘宝首页显示已登录在新标签页打开https://www.tmall.com/天猫首页显示已登录——你并没有在天猫输入过密码为什么淘宝taobao.com和天猫tmall.com是两个完全不同的域名浏览器有同源策略——A 网站的数据B 网站无法访问。那你在淘宝登录了天猫怎么知道的答案是单点登录Single Sign-On简称 SSO。2. 前置知识三个必须理解的概念在正式讲解之前有三个概念需要先弄清楚。它们是后续所有内容的基础。2.1 Cookie —— 浏览器替你保管的身份卡什么是 CookieCookie 是一小段文本数据由服务器发给浏览器浏览器保存在本地。之后每次访问同一个网站浏览器都会自动把这段数据带上。打个比方你去游乐场买了门票登录成功工作人员在你手腕上盖了一个章Cookie。之后你去玩每个项目时工作人员扫一眼你手腕上的章就放行自动携带 Cookie不需要你每次都重新买票重新登录。关键规则Cookie 是跟着域名走的taobao.com写入的 Cookie只有访问taobao.com时浏览器才会自动携带tmall.com的 Cookie只有访问tmall.com时才会携带A 网站无法读取B 网站的 Cookie这就是同源策略的保护浏览器的 Cookie 存储简化示意 ┌────────────────────────────────────────────────┐ │ .taobao.com │ │ SID TB_abc123 │ │ _tb_token_ xxxx │ ├────────────────────────────────────────────────┤ │ .tmall.com │ │ SID TM_def456 ← 什么时候写入的 │ │ _m_h5_tk yyyy 这就是本文要解答的问题 │ ├────────────────────────────────────────────────┤ │ .jd.com │ │ (空你没登录过京东) │ └────────────────────────────────────────────────┘2.2 Session —— 服务器端的身份档案Cookie 只是一把钥匙通常是一串无意义的随机字符真正的用户信息存在服务器的内存或数据库中这就是Session。你的 Cookie SID TM_def456 ← 只是一个编号钥匙 服务器的 Session 存储Redis/内存 TM_def456 → { ← 用编号查到的档案 userId: 9527, nick: 张三, loginTime: 2026-03-26 10:00:00, expireTime: 2026-03-27 10:00:00 }访问网站时发生的事浏览器自动带上 CookieSIDTM_def456服务器拿到TM_def456去 Session 存储中查找找到了 → 你是用户张三返回已登录页面没找到 → 你没登录返回未登录页面2.3 同源策略 —— 浏览器的防火墙浏览器规定不同域名之间的数据是隔离的。taobao.com的 JS不能直接读写tmall.com的 Cookietaobao.com的 JS不能直接调用tmall.com的接口除非对方允许这就是问题的核心既然隔离了淘宝登录后天猫的 Cookie 是怎么写入的答案就在下面。3. 全景概览一句话说清原理淘宝登录成功后登录页面的 JS 悄悄向pass.tmall.com发了一个请求伪装成加载图片这个请求携带了一个通行证Token。天猫服务器验证通行证后在响应中让浏览器在.tmall.com域下写入了登录 Cookie。用一张图概括┌──────────────────────────────────────┐ │ login.taobao.com │ │ (统一登录中心) │ │ │ │ 用户在这里输入账号密码 │ │ 登录成功后生成通行证(Token) │ │ 并把通行证分发给各子站 │ └──────────┬───────────┬────────────────┘ │ │ 发通行证给 │ │ 发通行证给 taobao.com│ │ tmall.com │ │ ▼ ▼ ┌──────────────┐ ┌──────────────┐ │ taobao.com │ │ tmall.com │ │ │ │ │ │ 验证通行证 ✓ │ │ 验证通行证 ✓ │ │ 写入自己的 │ │ 写入自己的 │ │ 登录Cookie │ │ 登录Cookie │ └──────────────┘ └──────────────┘核心理念集中登录分散写 Cookie。4. 完整流程六个阶段逐帧拆解阶段一访问淘宝发现未登录你做的事在浏览器地址栏输入https://www.taobao.com/并回车。背后发生的事┌─────────┐ ┌──────────────┐ │ 浏览器 │ ── GET https://www.taobao.com/ ────► │ taobao.com │ │ │ Cookie: (空没有登录态) │ 服务器 │ │ │ │ │ │ │ ◄── 返回 HTML 页面 ─────────────────── │ 检查 Cookie │ │ │ 页面中有登录按钮 │ → 没有登录态 │ │ │ 按钮链接指向: │ → 返回未登录 │ │ │ login.taobao.com/member/login.jhtml│ 状态的页面 │ │ │ ?redirectURLwww.taobao.com │ │ └─────────┘ └──────────────┘注意redirectURL这个参数——它告诉登录中心“登录成功后请把用户送回这个地址”。就像你去办事大厅排队先拿了一个号码牌redirectURL办完事后按号码牌找到你。此时浏览器的 Cookie 存储.taobao.com → (空或无效) .tmall.com → (空)阶段二跳转到统一登录中心你做的事点击页面上的登录按钮。背后发生的事┌─────────┐ ┌──────────────────┐ │ 浏览器 │ ── GET login.taobao.com/member/ │ login.taobao.com │ │ │ login.jhtml │ (统一登录中心) │ │ │ ?redirectURLwww.taobao.com ──────►│ │ │ │ │ 这是阿里所有网站 │ │ │ ◄── 返回登录页面 HTML ──────────────── │ 共用的登录入口 │ │ │ - 账号输入框 │ │ │ │ - 密码输入框 │ 也叫 Havana 系统 │ │ │ - 验证码/扫码 │ │ │ │ - 登录按钮 │ │ └─────────┘ └──────────────────┘为什么统一阿里旗下有几十个网站淘宝、天猫、1688、闲鱼、飞猪…如果每个网站各搞一套登录系统用户要注册几十个账号。所以阿里做了一个统一登录中心内部叫 Havana所有网站共用同一套账号体系。阶段三提交账号密码服务端验证你做的事在登录页面输入账号和密码点击登录按钮。背后发生的事详细拆解第①步前端加密密码你输入的密码: myPassword123 │ ▼ 前端 JS 用 RSA 公钥加密公钥是页面加载时服务器给的 │ ▼ 加密后: a3Fk9x2M... 一堆看不懂的字符为什么加密防止密码在网络传输中被人偷看即使用了 HTTPS多一层加密多一份保险。第②步发送登录请求┌─────────┐ ┌──────────────────┐ │ 浏览器 │ ── POST login.taobao.com/newlogin/ │ login.taobao.com │ │ │ login.do │ │ │ │ │ │ │ │ 请求体Body: │ │ │ │ { │ │ │ │ loginId: zhangsan, │ │ │ │ password2: a3Fk9x2M..., ←加密 │ │ │ │ ua: 设备指纹信息, │ │ │ │ } │ │ │ │ ─────────────────────────────────────►│ │ └─────────┘ └──────────────────┘第③步服务端验证Login Server 内部login.taobao.com 服务端收到请求后依次做以下事情 │ ┌───────────────┼────────────────────────────┐ │ ▼ │ │ 1. 用 RSA 私钥解密密码 │ │ a3Fk9x2M... → myPassword123 │ │ │ │ │ ▼ │ │ 2. 查数据库验证账号密码是否正确 │ │ SELECT * FROM users │ │ WHERE login_id zhangsan │ │ → 找到用户userId 9527 │ │ → 比对密码 hash ✓ 正确 │ │ │ │ │ ▼ │ │ 3. 风控检查 │ │ - 是否异地登录 │ │ - 设备是否可信 │ │ - 是否有暴力破解嫌疑 │ │ → 全部通过 ✓ │ │ │ │ │ ▼ │ │ 4. 创建全局会话Global Session │ │ 这是整个 SSO 的核心 │ │ 在统一的会话存储Redis中写入 │ │ │ │ ┌──────────────────────────────────────┐ │ │ │ key: GS_a1b2c3 (全局会话 ID) │ │ │ │ value: { │ │ │ │ userId: 9527, │ │ │ │ nick: 张三, │ │ │ │ loginTime: 2026-03-26 10:00:00, │ │ │ │ expireTime: 2026-03-27 10:00:00 │ │ │ │ } │ │ │ └──────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ 5. 生成一次性通行证 │ │ ST (Service Ticket) 加密 │ │ 全局会话ID 时间戳 随机数 签名 │ │ │ │ → ST ST_x7y8z9... │ │ │ │ │ ▼ │ │ 6. 生成各子站的跨域写Cookie地址列表 │ │ 把 ST 拼到每个子站的 URL 中 │ └─────────────────────────────────────────────┘第④步服务端返回登录结果┌─────────┐ ┌──────────────────┐ │ 浏览器 │ ◄── HTTP 200 OK ─────────────────── │ login.taobao.com │ │ │ │ │ │ │ 响应头Headers: │ │ │ │ Set-Cookie: JSESSIONIDxxx; │ ← 登录中心自己 │ │ │ Domain.taobao.com │ 域下的 Cookie │ │ │ │ │ │ │ 响应体Body, JSON格式: │ │ │ │ { │ │ │ │ ret: [SUCCESS::登录成功], │ │ │ │ data: { │ │ │ │ redirectURL: │ │ │ │ https://www.taobao.com/, │ │ │ │ │ │ │ │ token: ST_x7y8z9..., │ ← 一次性通行证 │ │ │ │ │ │ │ cookieUrls: [ │ ← ★ 最关键的部分 │ │ │ https://pass.tmall.com/add │ │ │ │ ?tokenST_x7y8z9... │ │ │ │ sig签名t时间戳, │ │ │ │ https://login.1688.com/add │ │ │ │ ?tokenST_x7y8z9... │ │ │ │ sig签名t时间戳, │ │ │ │ https://login.taobao.com/add │ │ │ │ ?tokenST_x7y8z9... │ │ │ │ sig签名t时间戳, │ │ │ │ https://pass.alitrip.com/add │ │ │ │ ?tokenST_x7y8z9... │ │ │ │ sig签名t时间戳 │ │ │ │ ] │ │ │ │ } │ │ │ │ } │ │ └─────────┘ └──────────────────┘cookieUrls就是分发名单——登录中心说请你浏览器拿着这个通行证分别去这些地址签到让它们各自在自己域下给你盖章写 Cookie。阶段四前端 JS 执行跨域 Cookie 同步最核心的步骤 {#stage-4}这是整个 SSO 中最关键、最巧妙的环节。发生在哪里发生在login.taobao.com这个登录页面中。该页面加载了一个 JS 文件比如https://g.alicdn.com/login/newlogin/x.x.x/index.js其中包含处理登录成功后逻辑的代码。什么时候触发在上一步阶段三第④步服务端返回 JSON 响应之后页面跳转之前。具体怎么做前端 JS 拿到cookieUrls数组后遍历数组中的每个 URL动态创建img标签来发请求。// // 这段代码运行在 login.taobao.com 的页面中// // 第①步收到登录成功的 JSON 响应constresponseawaitfetch(/newlogin/login.do,{method:POST,body:formData,});constresultawaitresponse.json();// result { ret: [SUCCESS::...], data: { cookieUrls: [...], redirectURL: ... } }// 第②步取出跨域写 Cookie 的 URL 列表const{cookieUrls,redirectURL}result.data;// 第③步逐个请求每个 URLconstpromisescookieUrls.map((url){returnnewPromise((resolve){// ★ 创建一个 img 标签constimgnewImage();// 图片加载完成不管成功失败就算搞定img.onloadresolve;img.onerrorresolve;// ★ 设置图片的 src 为跨域 URL// 浏览器看到 img.src 被赋值后会立即发出 GET 请求img.srcurl;// 此时浏览器发出// GET https://pass.tmall.com/add?tokenST_x7y8z9...sigxxxtxxx});});// 第④步等待所有请求完成awaitPromise.all(promises);// 第⑤步全部写完后跳转回淘宝window.location.hrefredirectURL;// 跳转到 https://www.taobao.com/为什么用img标签而不是fetch方式能跨域吗能接收 Set-Cookie 吗需要对方配合吗img src...✅ 天然跨域✅ 浏览器自动处理❌ 不需要 CORSfetch()⚠️ 需要 CORS⚠️ 需要特殊配置✅ 需要对方设置响应头script src...✅ 天然跨域✅ 可以❌ 不需要 CORSimg是最简单最兼容的方式浏览器加载图片时不受同源策略限制而且会正常处理响应中的Set-Cookie头。服务端返回一个 1×1 像素的透明 GIF 即可。这个请求到达 tmall.com 后发生了什么┌─────────┐ ┌───────────────────┐ │ 浏览器 │── GET https://pass.tmall.com/add ──►│ pass.tmall.com │ │ (img) │ ?tokenST_x7y8z9... │ (天猫的登录服务) │ │ │ sigHmacSHA256签名 │ │ │ │ t1711411205 │ │ │ │ │ │ │ │ ┌─── 服务端处理流程 ───────────┐ │ │ │ │ │ │ │ │ │ │ │ 1. 验签 │ │ │ │ │ │ 用密钥对 tokent 计算签名 │ │ │ │ │ │ 与传入的 sig 比对 │ │ │ │ │ │ → 一致 ✓没被篡改 │ │ │ │ │ │ │ │ │ │ │ │ 2. 检查时间戳 │ │ │ │ │ │ 当前时间 - t 5分钟 │ │ │ │ │ │ → 是 ✓没过期 │ │ │ │ │ │ │ │ │ │ │ │ 3. 验证 Token │ │ │ │ │ │ 向 Havana 全局会话中心查询 │ │ │ │ │ │ ST_x7y8z9... 是否有效 │ │ │ │ │ │ → 有效 ✓ │ │ │ │ │ │ → 返回 userId9527 │ │ │ │ │ │ │ │ │ │ │ │ 4. 标记 Token 已使用 │ │ │ │ │ │ 一次性的用完作废 │ │ │ │ │ │ │ │ │ │ │ │ 5. 为天猫创建本域 Session │ │ │ │ │ │ 在 Redis 中写入 │ │ │ │ │ │ ┌──────────────────────┐ │ │ │ │ │ │ │ key: TM_m9n8o7 │ │ │ │ │ │ │ │ value: { │ │ │ │ │ │ │ │ userId: 9527, │ │ │ │ │ │ │ │ nick: 张三, │ │ │ │ │ │ │ │ globalRef: │ │ │ │ │ │ │ │ GS_a1b2c3 │ │ │ │ │ │ │ │ } │ │ │ │ │ │ │ └──────────────────────┘ │ │ │ │ │ │ │ │ │ │ │ └──────────────────────────────┘ │ │ │ │ │ │ │ │ ◄── HTTP 200 ─────────────────────── │ │ │ │ │ │ │ │ 响应头: │ │ │ │ Set-Cookie: _m_h5_tk加密值; │ ← ★ 天猫域的Cookie │ │ │ Domain.tmall.com; │ │ │ │ Path/; │ │ │ │ HttpOnly; │ ← JS 无法读取 │ │ │ Secure │ ← 仅 HTTPS 传输 │ │ │ Set-Cookie: cookie2值; │ │ │ │ Domain.tmall.com │ │ │ │ Set-Cookie: _tb_token_CSRF值; │ │ │ │ Domain.tmall.com │ │ │ │ P3P: CPCAO PSA OUR │ ← 兼容旧版IE │ │ │ │ │ │ │ 响应体: │ │ │ │ (一个 1x1 像素的透明 GIF 图片) │ │ │ │ │ │ └─────────┘ └───────────────────┘ 浏览器收到响应后自动将 Cookie 存入 .tmall.com 域下 ┌────────────────────────────────────────────────┐ │ .tmall.com │ │ _m_h5_tk 加密值 ← 新写入 │ │ cookie2 值 ← 新写入 │ │ _tb_token_ CSRF值 ← 新写入 │ └────────────────────────────────────────────────┘同一时间其他子站也在做同样的事并发执行 ┌──────────────────┐ 浏览器(img) ──────────────│► pass.tmall.com │→ Set-Cookie .tmall.com ✓ │ │ 浏览器(img) ──────────────│► login.taobao.com │→ Set-Cookie .taobao.com ✓ │ │ 浏览器(img) ──────────────│► login.1688.com │→ Set-Cookie .1688.com ✓ │ │ 浏览器(img) ──────────────│► pass.alitrip.com │→ Set-Cookie .alitrip.com ✓ └──────────────────┘全部完成后浏览器的 Cookie 存储变成了┌────────────────────────────────────────────────┐ │ .taobao.com → SIDTB_abc123, ... ✓ 已有 │ │ .tmall.com → _m_h5_tk..., ... ✓ 新写入│ │ .1688.com → SIDAL_ghi789, ... ✓ 新写入│ │ .alitrip.com → SIDAT_jkl012, ... ✓ 新写入│ └────────────────────────────────────────────────┘阶段五跳转回淘宝首页所有cookieUrls请求完成后JS 执行window.location.href https://www.taobao.com/。┌─────────┐ ┌──────────────┐ │ 浏览器 │── GET https://www.taobao.com/ ──────►│ taobao.com │ │ │ Cookie: │ 服务器 │ │ │ SIDTB_abc123 ← 浏览器自动携带 │ │ │ │ _tb_token_xxx │ │ │ │ │ │ │ │ ◄── 已登录状态的淘宝首页 ────────────── │ 查 Session: │ │ │ 右上角显示张三 已登录 │ TB_abc123 │ │ │ │ → userId:9527│ └─────────┘ │ → 登录态有效 ✓│ └──────────────┘阶段六访问天猫发现已登录你做的事在新标签页输入https://www.tmall.com/并回车。背后发生的事┌─────────┐ ┌──────────────┐ │ 浏览器 │── GET https://www.tmall.com/ ───────►│ tmall.com │ │ │ Cookie: │ 服务器 │ │ │ _m_h5_tk加密值 ← 自动携带 │ │ │ │ cookie2值 ← 阶段四写入的 │ │ │ │ _tb_token_CSRF值 │ │ │ │ │ │ │ │ ┌─── tmall.com 服务端处理 ────────┐ │ │ │ │ │ │ │ │ │ │ │ 1. 读取 Cookie: _m_h5_tk │ │ │ │ │ │ 2. 解密得到 sessionId │ │ │ │ │ │ → TM_m9n8o7 │ │ │ │ │ │ 3. 在 Redis 中查找: │ │ │ │ │ │ Redis.get(TM_m9n8o7) │ │ │ │ │ │ → { │ │ │ │ │ │ userId: 9527, │ │ │ │ │ │ nick: 张三 │ │ │ │ │ │ } │ │ │ │ │ │ 4. 确认这是用户张三 ✓ │ │ │ │ │ │ 5. 返回已登录的天猫首页 │ │ │ │ │ └────────────────────────────────┘ │ │ │ │ │ │ │ │ ◄── 已登录状态的天猫首页 ────────────── │ │ │ │ 右上角显示张三 已登录 │ │ └─────────┘ └──────────────┘你没有在天猫输入过密码但你已经登录了。这就是跨域 SSO 的完整过程。5. 深入理解你可能还会问的问题Q1后端怎么区分不同用户每个用户浏览器中存储的 Cookie 值不同。张三的浏览器Cookie: _m_h5_tkAAA → 服务端查到 userId9527 → 张三 李四的浏览器Cookie: _m_h5_tkBBB → 服务端查到 userId6688 → 李四 没登录的人 Cookie: (空) → 服务端查不到 → 未登录就像每个人手腕上盖的章Cookie图案不同工作人员服务器根据图案查花名册Session就知道你是谁。Q2Cookie 是谁带上去的我的代码里没写啊浏览器自动做的。这是 HTTP 协议的标准行为服务端在响应中说Set-Cookie: xxxyyy; Domain.tmall.com浏览器把xxxyyy存到.tmall.com域下之后凡是访问*.tmall.com的请求浏览器自动在请求头加上Cookie: xxxyyy你不需要写任何代码这是浏览器的内置行为。Q3别人能伪造我的 Cookie 来冒充我吗非常困难原因防护措施效果Session ID 是随机的 128 位以上字符串暴力猜测几乎不可能比宇宙中的原子数还多HttpOnly标记JS 代码无法读取这个 CookieXSS 攻击偷不到Secure标记只在 HTTPS 下传输中间人偷听不到Session 有过期时间即使泄露了过几小时就自动失效Q4Token通行证和 Session ID 有什么区别Token (ST_x7y8z9...) │ ├─ 谁生成的→ 登录中心 (login.taobao.com) │ ├─ 用在哪 → 跨域写 Cookie 时作为介绍信 │ ├─ 用几次 → 一次性用完即废 │ └─ 有效期 → 极短几分钟 Session ID (TM_m9n8o7) ├─ 谁生成的→ 各子站 (tmall.com / taobao.com) ├─ 用在哪 → 存在 Cookie 中每次访问都带上 ├─ 用几次 → 反复使用直到过期 └─ 有效期 → 较长24小时或更久打个比方Token 是一封一次性的介绍信Session ID 是一张长期的会员卡。你拿着介绍信去天猫办了一张会员卡之后每次去天猫出示会员卡就行介绍信已经作废了。Q5如果我在淘宝退出登录天猫会自动退出吗是的。退出时也有类似的机制你在淘宝点击退出请求到达登录中心销毁全局 Session登录中心同样通过跨域请求通知各子站清除 Cookie各子站销毁本域 Session清除 Cookie下次访问天猫时Cookie 已失效 → 未登录6. 安全机制为什么这套方案不会被攻破6.1 通行证Token的安全设计Token 组成ST encrypt(globalSessionId timestamp random signature) 防伪造有 HMAC 签名密钥只有服务端知道 → 攻击者无法伪造一个合法的 Token 防重放一次性使用用后立即标记为已消费 → 即使截获了 Token也无法再次使用 防过期包含时间戳超过 5 分钟自动失效 → 即使截获了 Token 但没来得及用也过期了 防篡改URL 中包含签名 (sig)修改任何参数都会导致验签失败 → 攻击者无法修改 Token 里的用户 ID6.2 Cookie 的安全设计Set-Cookie: _m_h5_tk加密值; Domain.tmall.com; → 只在 tmall.com 域下生效 Path/; → 整站有效 HttpOnly; → JavaScript 无法读取防 XSS Secure; → 仅 HTTPS 传输防中间人 SameSiteLax; → 限制第三方场景发送防 CSRF6.3 整体安全链条密码安全 传输安全 存储安全 使用安全 ──────── ──────── ──────── ──────── 用户密码 → RSA加密 → HTTPS传输 → 服务端解密 → bcrypt 哈希比对 → 不存明文 Token → 签名防伪 → HTTPS传输 → 一次性使用 → 5分钟过期 → 用完销毁 Cookie → 加密值 → Secure → HttpOnly → SameSite → 定期过期7. 现代浏览器的挑战与应对近年来浏览器对第三方 Cookie 的限制越来越严格对 SSO 产生了影响问题浏览器限制Safari (ITP)默认阻止所有第三方 CookieChrome计划逐步淘汰第三方 CookieSameSiteLax 默认值Firefox (ETP)默认阻止已知跟踪器的第三方 Cookieimg跨域写 Cookie 的方式在这些限制下可能失效。应对方案阿里等大型平台在演进中采用以下替代方案方案一302 重定向链目前最主流 ────────────────────────────── 登录成功后不是用 img 并发请求而是通过 302 跳转链 login.taobao.com → 302 跳转到 pass.tmall.com/sso?tokenST_xxx → tmall.com 服务端写入 Cookie此时是第一方上下文不受限制 → 302 跳转到 login.1688.com/sso?tokenST_xxx → 1688.com 服务端写入 Cookie → 302 跳转到 ... 最终跳回 taobao.com 每一跳都是直接访问目标域Cookie 在第一方上下文中写入不受第三方限制。 缺点是串行的比并发 img 慢一些。 方案二Storage Access API ────────────────────────── 使用浏览器提供的 document.requestStorageAccess() API 在用户授权后获得存储访问权限。 方案三后端 Token 传递 ────────────────────── 不依赖 Cookie通过 URL 参数传递 Token 由目标站点的前端 JS 存入 localStorage 后续请求在 Header 中手动携带。8. 动手验证自己看一遍整个过程如果你想亲眼看到这一切可以按以下步骤操作步骤 1打开 Chrome 开发者工具打开 Chrome 浏览器按F12或CmdOptionI(Mac) 打开开发者工具切到Network网络面板勾选Preserve log保留日志这样页面跳转后历史请求不会消失步骤 2清除登录状态清除taobao.com和tmall.com的 Cookie开发者工具 → Application → Cookies → 逐个清除步骤 3登录并观察访问https://www.taobao.com/点击登录跳转到login.taobao.com输入账号密码点击登录步骤 4在 Network 面板中寻找关键请求登录成功的瞬间你会看到① POST login.taobao.com/newlogin/login.do ← 提交登录请求 状态200 响应JSON包含 cookieUrls ② GET pass.tmall.com/add?tokenST_... ← 跨域写 Cookie 类型img 状态200 响应头中有 Set-Cookie ③ GET login.1688.com/add?tokenST_... ← 另一个子站 类型img ④ GET login.taobao.com/add?tokenST_... ← 淘宝自己 类型img ⑤ (页面跳转到 www.taobao.com)点开第 ② 条请求在Response Headers中你能看到Set-Cookie头在Request URL中你能看到 Token 参数。9. 总结一张图回顾全过程你的浏览器 login.taobao.com pass.tmall.com Havana(统一会话) │ (统一登录中心) (天猫登录服务) (Redis) │ │ │ │ ① │── 点击登录 ──────────────►│ │ │ │◄── 返回登录表单 ──────────│ │ │ │ │ │ │ ② │── 提交账号密码 ──────────►│ │ │ │ │── 创建全局Session ──────────────────────────►│ │ │◄── 返回 SessionId ──────────────────────────│ │◄── 返回 JSON ────────────│ │ │ │ (token cookieUrls) │ │ │ │ │ │ │ ③ │── img GET /add?token ──────────────────────────►│ │ │ │ │── 验证 Token ──────►│ │ │ │◄── OK, 用户9527 ───│ │ │ │── 创建天猫 Session ►│ │◄── Set-Cookie (.tmall.com) ◄────────────────────── │ │ │ │ │ │ ④ │── 跳转回 taobao.com ────►│ │ │ │◄── 淘宝已登录首页 ────────│ │ │ │ │ │ │ │ ... 过了一会儿 ... │ │ │ │ │ │ │ ⑤ │── 访问 tmall.com ──────────────────────────────────►│ │ │ Cookie: _m_h5_tkxxx │ │── 查 Session ──────►│ │ (浏览器自动携带) │ │◄── 用户9527 ────────│ │◄── 天猫已登录首页 ◄──────────────────────────────── │ │ │ │ │ │三句话概括阿里有一个统一登录中心login.taobao.com所有网站共用同一套账号登录成功后登录页面的 JS 通过img标签悄悄向各子站发请求把通行证传过去各子站验证后在自己域下写入登录 Cookie之后你访问任一子站浏览器自动携带该站的 Cookie服务端通过 Cookie 查到你的身份无需再次登录本文基于阿里 SSO 的公开原理进行分析。实际实现中字段名、加密算法、URL 格式等细节可能有所不同但核心架构和原理一致。
淘宝 × 天猫 跨域单点登录(SSO)全链路解析
发布时间:2026/5/19 8:01:23
一份面向零基础读者的技术文档。读完后你将完全理解为什么在淘宝登录后打开天猫发现自己也登录了。目录现象描述前置知识三个必须理解的概念全景概览一句话说清原理完整流程六个阶段逐帧拆解深入理解你可能还会问的问题安全机制为什么这套方案不会被攻破现代浏览器的挑战与应对动手验证自己看一遍整个过程总结1. 现象描述你打开浏览器依次做了以下操作访问https://www.taobao.com/页面显示未登录点击登录跳转到https://login.taobao.com/输入账号密码登录成功自动跳回淘宝首页显示已登录在新标签页打开https://www.tmall.com/天猫首页显示已登录——你并没有在天猫输入过密码为什么淘宝taobao.com和天猫tmall.com是两个完全不同的域名浏览器有同源策略——A 网站的数据B 网站无法访问。那你在淘宝登录了天猫怎么知道的答案是单点登录Single Sign-On简称 SSO。2. 前置知识三个必须理解的概念在正式讲解之前有三个概念需要先弄清楚。它们是后续所有内容的基础。2.1 Cookie —— 浏览器替你保管的身份卡什么是 CookieCookie 是一小段文本数据由服务器发给浏览器浏览器保存在本地。之后每次访问同一个网站浏览器都会自动把这段数据带上。打个比方你去游乐场买了门票登录成功工作人员在你手腕上盖了一个章Cookie。之后你去玩每个项目时工作人员扫一眼你手腕上的章就放行自动携带 Cookie不需要你每次都重新买票重新登录。关键规则Cookie 是跟着域名走的taobao.com写入的 Cookie只有访问taobao.com时浏览器才会自动携带tmall.com的 Cookie只有访问tmall.com时才会携带A 网站无法读取B 网站的 Cookie这就是同源策略的保护浏览器的 Cookie 存储简化示意 ┌────────────────────────────────────────────────┐ │ .taobao.com │ │ SID TB_abc123 │ │ _tb_token_ xxxx │ ├────────────────────────────────────────────────┤ │ .tmall.com │ │ SID TM_def456 ← 什么时候写入的 │ │ _m_h5_tk yyyy 这就是本文要解答的问题 │ ├────────────────────────────────────────────────┤ │ .jd.com │ │ (空你没登录过京东) │ └────────────────────────────────────────────────┘2.2 Session —— 服务器端的身份档案Cookie 只是一把钥匙通常是一串无意义的随机字符真正的用户信息存在服务器的内存或数据库中这就是Session。你的 Cookie SID TM_def456 ← 只是一个编号钥匙 服务器的 Session 存储Redis/内存 TM_def456 → { ← 用编号查到的档案 userId: 9527, nick: 张三, loginTime: 2026-03-26 10:00:00, expireTime: 2026-03-27 10:00:00 }访问网站时发生的事浏览器自动带上 CookieSIDTM_def456服务器拿到TM_def456去 Session 存储中查找找到了 → 你是用户张三返回已登录页面没找到 → 你没登录返回未登录页面2.3 同源策略 —— 浏览器的防火墙浏览器规定不同域名之间的数据是隔离的。taobao.com的 JS不能直接读写tmall.com的 Cookietaobao.com的 JS不能直接调用tmall.com的接口除非对方允许这就是问题的核心既然隔离了淘宝登录后天猫的 Cookie 是怎么写入的答案就在下面。3. 全景概览一句话说清原理淘宝登录成功后登录页面的 JS 悄悄向pass.tmall.com发了一个请求伪装成加载图片这个请求携带了一个通行证Token。天猫服务器验证通行证后在响应中让浏览器在.tmall.com域下写入了登录 Cookie。用一张图概括┌──────────────────────────────────────┐ │ login.taobao.com │ │ (统一登录中心) │ │ │ │ 用户在这里输入账号密码 │ │ 登录成功后生成通行证(Token) │ │ 并把通行证分发给各子站 │ └──────────┬───────────┬────────────────┘ │ │ 发通行证给 │ │ 发通行证给 taobao.com│ │ tmall.com │ │ ▼ ▼ ┌──────────────┐ ┌──────────────┐ │ taobao.com │ │ tmall.com │ │ │ │ │ │ 验证通行证 ✓ │ │ 验证通行证 ✓ │ │ 写入自己的 │ │ 写入自己的 │ │ 登录Cookie │ │ 登录Cookie │ └──────────────┘ └──────────────┘核心理念集中登录分散写 Cookie。4. 完整流程六个阶段逐帧拆解阶段一访问淘宝发现未登录你做的事在浏览器地址栏输入https://www.taobao.com/并回车。背后发生的事┌─────────┐ ┌──────────────┐ │ 浏览器 │ ── GET https://www.taobao.com/ ────► │ taobao.com │ │ │ Cookie: (空没有登录态) │ 服务器 │ │ │ │ │ │ │ ◄── 返回 HTML 页面 ─────────────────── │ 检查 Cookie │ │ │ 页面中有登录按钮 │ → 没有登录态 │ │ │ 按钮链接指向: │ → 返回未登录 │ │ │ login.taobao.com/member/login.jhtml│ 状态的页面 │ │ │ ?redirectURLwww.taobao.com │ │ └─────────┘ └──────────────┘注意redirectURL这个参数——它告诉登录中心“登录成功后请把用户送回这个地址”。就像你去办事大厅排队先拿了一个号码牌redirectURL办完事后按号码牌找到你。此时浏览器的 Cookie 存储.taobao.com → (空或无效) .tmall.com → (空)阶段二跳转到统一登录中心你做的事点击页面上的登录按钮。背后发生的事┌─────────┐ ┌──────────────────┐ │ 浏览器 │ ── GET login.taobao.com/member/ │ login.taobao.com │ │ │ login.jhtml │ (统一登录中心) │ │ │ ?redirectURLwww.taobao.com ──────►│ │ │ │ │ 这是阿里所有网站 │ │ │ ◄── 返回登录页面 HTML ──────────────── │ 共用的登录入口 │ │ │ - 账号输入框 │ │ │ │ - 密码输入框 │ 也叫 Havana 系统 │ │ │ - 验证码/扫码 │ │ │ │ - 登录按钮 │ │ └─────────┘ └──────────────────┘为什么统一阿里旗下有几十个网站淘宝、天猫、1688、闲鱼、飞猪…如果每个网站各搞一套登录系统用户要注册几十个账号。所以阿里做了一个统一登录中心内部叫 Havana所有网站共用同一套账号体系。阶段三提交账号密码服务端验证你做的事在登录页面输入账号和密码点击登录按钮。背后发生的事详细拆解第①步前端加密密码你输入的密码: myPassword123 │ ▼ 前端 JS 用 RSA 公钥加密公钥是页面加载时服务器给的 │ ▼ 加密后: a3Fk9x2M... 一堆看不懂的字符为什么加密防止密码在网络传输中被人偷看即使用了 HTTPS多一层加密多一份保险。第②步发送登录请求┌─────────┐ ┌──────────────────┐ │ 浏览器 │ ── POST login.taobao.com/newlogin/ │ login.taobao.com │ │ │ login.do │ │ │ │ │ │ │ │ 请求体Body: │ │ │ │ { │ │ │ │ loginId: zhangsan, │ │ │ │ password2: a3Fk9x2M..., ←加密 │ │ │ │ ua: 设备指纹信息, │ │ │ │ } │ │ │ │ ─────────────────────────────────────►│ │ └─────────┘ └──────────────────┘第③步服务端验证Login Server 内部login.taobao.com 服务端收到请求后依次做以下事情 │ ┌───────────────┼────────────────────────────┐ │ ▼ │ │ 1. 用 RSA 私钥解密密码 │ │ a3Fk9x2M... → myPassword123 │ │ │ │ │ ▼ │ │ 2. 查数据库验证账号密码是否正确 │ │ SELECT * FROM users │ │ WHERE login_id zhangsan │ │ → 找到用户userId 9527 │ │ → 比对密码 hash ✓ 正确 │ │ │ │ │ ▼ │ │ 3. 风控检查 │ │ - 是否异地登录 │ │ - 设备是否可信 │ │ - 是否有暴力破解嫌疑 │ │ → 全部通过 ✓ │ │ │ │ │ ▼ │ │ 4. 创建全局会话Global Session │ │ 这是整个 SSO 的核心 │ │ 在统一的会话存储Redis中写入 │ │ │ │ ┌──────────────────────────────────────┐ │ │ │ key: GS_a1b2c3 (全局会话 ID) │ │ │ │ value: { │ │ │ │ userId: 9527, │ │ │ │ nick: 张三, │ │ │ │ loginTime: 2026-03-26 10:00:00, │ │ │ │ expireTime: 2026-03-27 10:00:00 │ │ │ │ } │ │ │ └──────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ 5. 生成一次性通行证 │ │ ST (Service Ticket) 加密 │ │ 全局会话ID 时间戳 随机数 签名 │ │ │ │ → ST ST_x7y8z9... │ │ │ │ │ ▼ │ │ 6. 生成各子站的跨域写Cookie地址列表 │ │ 把 ST 拼到每个子站的 URL 中 │ └─────────────────────────────────────────────┘第④步服务端返回登录结果┌─────────┐ ┌──────────────────┐ │ 浏览器 │ ◄── HTTP 200 OK ─────────────────── │ login.taobao.com │ │ │ │ │ │ │ 响应头Headers: │ │ │ │ Set-Cookie: JSESSIONIDxxx; │ ← 登录中心自己 │ │ │ Domain.taobao.com │ 域下的 Cookie │ │ │ │ │ │ │ 响应体Body, JSON格式: │ │ │ │ { │ │ │ │ ret: [SUCCESS::登录成功], │ │ │ │ data: { │ │ │ │ redirectURL: │ │ │ │ https://www.taobao.com/, │ │ │ │ │ │ │ │ token: ST_x7y8z9..., │ ← 一次性通行证 │ │ │ │ │ │ │ cookieUrls: [ │ ← ★ 最关键的部分 │ │ │ https://pass.tmall.com/add │ │ │ │ ?tokenST_x7y8z9... │ │ │ │ sig签名t时间戳, │ │ │ │ https://login.1688.com/add │ │ │ │ ?tokenST_x7y8z9... │ │ │ │ sig签名t时间戳, │ │ │ │ https://login.taobao.com/add │ │ │ │ ?tokenST_x7y8z9... │ │ │ │ sig签名t时间戳, │ │ │ │ https://pass.alitrip.com/add │ │ │ │ ?tokenST_x7y8z9... │ │ │ │ sig签名t时间戳 │ │ │ │ ] │ │ │ │ } │ │ │ │ } │ │ └─────────┘ └──────────────────┘cookieUrls就是分发名单——登录中心说请你浏览器拿着这个通行证分别去这些地址签到让它们各自在自己域下给你盖章写 Cookie。阶段四前端 JS 执行跨域 Cookie 同步最核心的步骤 {#stage-4}这是整个 SSO 中最关键、最巧妙的环节。发生在哪里发生在login.taobao.com这个登录页面中。该页面加载了一个 JS 文件比如https://g.alicdn.com/login/newlogin/x.x.x/index.js其中包含处理登录成功后逻辑的代码。什么时候触发在上一步阶段三第④步服务端返回 JSON 响应之后页面跳转之前。具体怎么做前端 JS 拿到cookieUrls数组后遍历数组中的每个 URL动态创建img标签来发请求。// // 这段代码运行在 login.taobao.com 的页面中// // 第①步收到登录成功的 JSON 响应constresponseawaitfetch(/newlogin/login.do,{method:POST,body:formData,});constresultawaitresponse.json();// result { ret: [SUCCESS::...], data: { cookieUrls: [...], redirectURL: ... } }// 第②步取出跨域写 Cookie 的 URL 列表const{cookieUrls,redirectURL}result.data;// 第③步逐个请求每个 URLconstpromisescookieUrls.map((url){returnnewPromise((resolve){// ★ 创建一个 img 标签constimgnewImage();// 图片加载完成不管成功失败就算搞定img.onloadresolve;img.onerrorresolve;// ★ 设置图片的 src 为跨域 URL// 浏览器看到 img.src 被赋值后会立即发出 GET 请求img.srcurl;// 此时浏览器发出// GET https://pass.tmall.com/add?tokenST_x7y8z9...sigxxxtxxx});});// 第④步等待所有请求完成awaitPromise.all(promises);// 第⑤步全部写完后跳转回淘宝window.location.hrefredirectURL;// 跳转到 https://www.taobao.com/为什么用img标签而不是fetch方式能跨域吗能接收 Set-Cookie 吗需要对方配合吗img src...✅ 天然跨域✅ 浏览器自动处理❌ 不需要 CORSfetch()⚠️ 需要 CORS⚠️ 需要特殊配置✅ 需要对方设置响应头script src...✅ 天然跨域✅ 可以❌ 不需要 CORSimg是最简单最兼容的方式浏览器加载图片时不受同源策略限制而且会正常处理响应中的Set-Cookie头。服务端返回一个 1×1 像素的透明 GIF 即可。这个请求到达 tmall.com 后发生了什么┌─────────┐ ┌───────────────────┐ │ 浏览器 │── GET https://pass.tmall.com/add ──►│ pass.tmall.com │ │ (img) │ ?tokenST_x7y8z9... │ (天猫的登录服务) │ │ │ sigHmacSHA256签名 │ │ │ │ t1711411205 │ │ │ │ │ │ │ │ ┌─── 服务端处理流程 ───────────┐ │ │ │ │ │ │ │ │ │ │ │ 1. 验签 │ │ │ │ │ │ 用密钥对 tokent 计算签名 │ │ │ │ │ │ 与传入的 sig 比对 │ │ │ │ │ │ → 一致 ✓没被篡改 │ │ │ │ │ │ │ │ │ │ │ │ 2. 检查时间戳 │ │ │ │ │ │ 当前时间 - t 5分钟 │ │ │ │ │ │ → 是 ✓没过期 │ │ │ │ │ │ │ │ │ │ │ │ 3. 验证 Token │ │ │ │ │ │ 向 Havana 全局会话中心查询 │ │ │ │ │ │ ST_x7y8z9... 是否有效 │ │ │ │ │ │ → 有效 ✓ │ │ │ │ │ │ → 返回 userId9527 │ │ │ │ │ │ │ │ │ │ │ │ 4. 标记 Token 已使用 │ │ │ │ │ │ 一次性的用完作废 │ │ │ │ │ │ │ │ │ │ │ │ 5. 为天猫创建本域 Session │ │ │ │ │ │ 在 Redis 中写入 │ │ │ │ │ │ ┌──────────────────────┐ │ │ │ │ │ │ │ key: TM_m9n8o7 │ │ │ │ │ │ │ │ value: { │ │ │ │ │ │ │ │ userId: 9527, │ │ │ │ │ │ │ │ nick: 张三, │ │ │ │ │ │ │ │ globalRef: │ │ │ │ │ │ │ │ GS_a1b2c3 │ │ │ │ │ │ │ │ } │ │ │ │ │ │ │ └──────────────────────┘ │ │ │ │ │ │ │ │ │ │ │ └──────────────────────────────┘ │ │ │ │ │ │ │ │ ◄── HTTP 200 ─────────────────────── │ │ │ │ │ │ │ │ 响应头: │ │ │ │ Set-Cookie: _m_h5_tk加密值; │ ← ★ 天猫域的Cookie │ │ │ Domain.tmall.com; │ │ │ │ Path/; │ │ │ │ HttpOnly; │ ← JS 无法读取 │ │ │ Secure │ ← 仅 HTTPS 传输 │ │ │ Set-Cookie: cookie2值; │ │ │ │ Domain.tmall.com │ │ │ │ Set-Cookie: _tb_token_CSRF值; │ │ │ │ Domain.tmall.com │ │ │ │ P3P: CPCAO PSA OUR │ ← 兼容旧版IE │ │ │ │ │ │ │ 响应体: │ │ │ │ (一个 1x1 像素的透明 GIF 图片) │ │ │ │ │ │ └─────────┘ └───────────────────┘ 浏览器收到响应后自动将 Cookie 存入 .tmall.com 域下 ┌────────────────────────────────────────────────┐ │ .tmall.com │ │ _m_h5_tk 加密值 ← 新写入 │ │ cookie2 值 ← 新写入 │ │ _tb_token_ CSRF值 ← 新写入 │ └────────────────────────────────────────────────┘同一时间其他子站也在做同样的事并发执行 ┌──────────────────┐ 浏览器(img) ──────────────│► pass.tmall.com │→ Set-Cookie .tmall.com ✓ │ │ 浏览器(img) ──────────────│► login.taobao.com │→ Set-Cookie .taobao.com ✓ │ │ 浏览器(img) ──────────────│► login.1688.com │→ Set-Cookie .1688.com ✓ │ │ 浏览器(img) ──────────────│► pass.alitrip.com │→ Set-Cookie .alitrip.com ✓ └──────────────────┘全部完成后浏览器的 Cookie 存储变成了┌────────────────────────────────────────────────┐ │ .taobao.com → SIDTB_abc123, ... ✓ 已有 │ │ .tmall.com → _m_h5_tk..., ... ✓ 新写入│ │ .1688.com → SIDAL_ghi789, ... ✓ 新写入│ │ .alitrip.com → SIDAT_jkl012, ... ✓ 新写入│ └────────────────────────────────────────────────┘阶段五跳转回淘宝首页所有cookieUrls请求完成后JS 执行window.location.href https://www.taobao.com/。┌─────────┐ ┌──────────────┐ │ 浏览器 │── GET https://www.taobao.com/ ──────►│ taobao.com │ │ │ Cookie: │ 服务器 │ │ │ SIDTB_abc123 ← 浏览器自动携带 │ │ │ │ _tb_token_xxx │ │ │ │ │ │ │ │ ◄── 已登录状态的淘宝首页 ────────────── │ 查 Session: │ │ │ 右上角显示张三 已登录 │ TB_abc123 │ │ │ │ → userId:9527│ └─────────┘ │ → 登录态有效 ✓│ └──────────────┘阶段六访问天猫发现已登录你做的事在新标签页输入https://www.tmall.com/并回车。背后发生的事┌─────────┐ ┌──────────────┐ │ 浏览器 │── GET https://www.tmall.com/ ───────►│ tmall.com │ │ │ Cookie: │ 服务器 │ │ │ _m_h5_tk加密值 ← 自动携带 │ │ │ │ cookie2值 ← 阶段四写入的 │ │ │ │ _tb_token_CSRF值 │ │ │ │ │ │ │ │ ┌─── tmall.com 服务端处理 ────────┐ │ │ │ │ │ │ │ │ │ │ │ 1. 读取 Cookie: _m_h5_tk │ │ │ │ │ │ 2. 解密得到 sessionId │ │ │ │ │ │ → TM_m9n8o7 │ │ │ │ │ │ 3. 在 Redis 中查找: │ │ │ │ │ │ Redis.get(TM_m9n8o7) │ │ │ │ │ │ → { │ │ │ │ │ │ userId: 9527, │ │ │ │ │ │ nick: 张三 │ │ │ │ │ │ } │ │ │ │ │ │ 4. 确认这是用户张三 ✓ │ │ │ │ │ │ 5. 返回已登录的天猫首页 │ │ │ │ │ └────────────────────────────────┘ │ │ │ │ │ │ │ │ ◄── 已登录状态的天猫首页 ────────────── │ │ │ │ 右上角显示张三 已登录 │ │ └─────────┘ └──────────────┘你没有在天猫输入过密码但你已经登录了。这就是跨域 SSO 的完整过程。5. 深入理解你可能还会问的问题Q1后端怎么区分不同用户每个用户浏览器中存储的 Cookie 值不同。张三的浏览器Cookie: _m_h5_tkAAA → 服务端查到 userId9527 → 张三 李四的浏览器Cookie: _m_h5_tkBBB → 服务端查到 userId6688 → 李四 没登录的人 Cookie: (空) → 服务端查不到 → 未登录就像每个人手腕上盖的章Cookie图案不同工作人员服务器根据图案查花名册Session就知道你是谁。Q2Cookie 是谁带上去的我的代码里没写啊浏览器自动做的。这是 HTTP 协议的标准行为服务端在响应中说Set-Cookie: xxxyyy; Domain.tmall.com浏览器把xxxyyy存到.tmall.com域下之后凡是访问*.tmall.com的请求浏览器自动在请求头加上Cookie: xxxyyy你不需要写任何代码这是浏览器的内置行为。Q3别人能伪造我的 Cookie 来冒充我吗非常困难原因防护措施效果Session ID 是随机的 128 位以上字符串暴力猜测几乎不可能比宇宙中的原子数还多HttpOnly标记JS 代码无法读取这个 CookieXSS 攻击偷不到Secure标记只在 HTTPS 下传输中间人偷听不到Session 有过期时间即使泄露了过几小时就自动失效Q4Token通行证和 Session ID 有什么区别Token (ST_x7y8z9...) │ ├─ 谁生成的→ 登录中心 (login.taobao.com) │ ├─ 用在哪 → 跨域写 Cookie 时作为介绍信 │ ├─ 用几次 → 一次性用完即废 │ └─ 有效期 → 极短几分钟 Session ID (TM_m9n8o7) ├─ 谁生成的→ 各子站 (tmall.com / taobao.com) ├─ 用在哪 → 存在 Cookie 中每次访问都带上 ├─ 用几次 → 反复使用直到过期 └─ 有效期 → 较长24小时或更久打个比方Token 是一封一次性的介绍信Session ID 是一张长期的会员卡。你拿着介绍信去天猫办了一张会员卡之后每次去天猫出示会员卡就行介绍信已经作废了。Q5如果我在淘宝退出登录天猫会自动退出吗是的。退出时也有类似的机制你在淘宝点击退出请求到达登录中心销毁全局 Session登录中心同样通过跨域请求通知各子站清除 Cookie各子站销毁本域 Session清除 Cookie下次访问天猫时Cookie 已失效 → 未登录6. 安全机制为什么这套方案不会被攻破6.1 通行证Token的安全设计Token 组成ST encrypt(globalSessionId timestamp random signature) 防伪造有 HMAC 签名密钥只有服务端知道 → 攻击者无法伪造一个合法的 Token 防重放一次性使用用后立即标记为已消费 → 即使截获了 Token也无法再次使用 防过期包含时间戳超过 5 分钟自动失效 → 即使截获了 Token 但没来得及用也过期了 防篡改URL 中包含签名 (sig)修改任何参数都会导致验签失败 → 攻击者无法修改 Token 里的用户 ID6.2 Cookie 的安全设计Set-Cookie: _m_h5_tk加密值; Domain.tmall.com; → 只在 tmall.com 域下生效 Path/; → 整站有效 HttpOnly; → JavaScript 无法读取防 XSS Secure; → 仅 HTTPS 传输防中间人 SameSiteLax; → 限制第三方场景发送防 CSRF6.3 整体安全链条密码安全 传输安全 存储安全 使用安全 ──────── ──────── ──────── ──────── 用户密码 → RSA加密 → HTTPS传输 → 服务端解密 → bcrypt 哈希比对 → 不存明文 Token → 签名防伪 → HTTPS传输 → 一次性使用 → 5分钟过期 → 用完销毁 Cookie → 加密值 → Secure → HttpOnly → SameSite → 定期过期7. 现代浏览器的挑战与应对近年来浏览器对第三方 Cookie 的限制越来越严格对 SSO 产生了影响问题浏览器限制Safari (ITP)默认阻止所有第三方 CookieChrome计划逐步淘汰第三方 CookieSameSiteLax 默认值Firefox (ETP)默认阻止已知跟踪器的第三方 Cookieimg跨域写 Cookie 的方式在这些限制下可能失效。应对方案阿里等大型平台在演进中采用以下替代方案方案一302 重定向链目前最主流 ────────────────────────────── 登录成功后不是用 img 并发请求而是通过 302 跳转链 login.taobao.com → 302 跳转到 pass.tmall.com/sso?tokenST_xxx → tmall.com 服务端写入 Cookie此时是第一方上下文不受限制 → 302 跳转到 login.1688.com/sso?tokenST_xxx → 1688.com 服务端写入 Cookie → 302 跳转到 ... 最终跳回 taobao.com 每一跳都是直接访问目标域Cookie 在第一方上下文中写入不受第三方限制。 缺点是串行的比并发 img 慢一些。 方案二Storage Access API ────────────────────────── 使用浏览器提供的 document.requestStorageAccess() API 在用户授权后获得存储访问权限。 方案三后端 Token 传递 ────────────────────── 不依赖 Cookie通过 URL 参数传递 Token 由目标站点的前端 JS 存入 localStorage 后续请求在 Header 中手动携带。8. 动手验证自己看一遍整个过程如果你想亲眼看到这一切可以按以下步骤操作步骤 1打开 Chrome 开发者工具打开 Chrome 浏览器按F12或CmdOptionI(Mac) 打开开发者工具切到Network网络面板勾选Preserve log保留日志这样页面跳转后历史请求不会消失步骤 2清除登录状态清除taobao.com和tmall.com的 Cookie开发者工具 → Application → Cookies → 逐个清除步骤 3登录并观察访问https://www.taobao.com/点击登录跳转到login.taobao.com输入账号密码点击登录步骤 4在 Network 面板中寻找关键请求登录成功的瞬间你会看到① POST login.taobao.com/newlogin/login.do ← 提交登录请求 状态200 响应JSON包含 cookieUrls ② GET pass.tmall.com/add?tokenST_... ← 跨域写 Cookie 类型img 状态200 响应头中有 Set-Cookie ③ GET login.1688.com/add?tokenST_... ← 另一个子站 类型img ④ GET login.taobao.com/add?tokenST_... ← 淘宝自己 类型img ⑤ (页面跳转到 www.taobao.com)点开第 ② 条请求在Response Headers中你能看到Set-Cookie头在Request URL中你能看到 Token 参数。9. 总结一张图回顾全过程你的浏览器 login.taobao.com pass.tmall.com Havana(统一会话) │ (统一登录中心) (天猫登录服务) (Redis) │ │ │ │ ① │── 点击登录 ──────────────►│ │ │ │◄── 返回登录表单 ──────────│ │ │ │ │ │ │ ② │── 提交账号密码 ──────────►│ │ │ │ │── 创建全局Session ──────────────────────────►│ │ │◄── 返回 SessionId ──────────────────────────│ │◄── 返回 JSON ────────────│ │ │ │ (token cookieUrls) │ │ │ │ │ │ │ ③ │── img GET /add?token ──────────────────────────►│ │ │ │ │── 验证 Token ──────►│ │ │ │◄── OK, 用户9527 ───│ │ │ │── 创建天猫 Session ►│ │◄── Set-Cookie (.tmall.com) ◄────────────────────── │ │ │ │ │ │ ④ │── 跳转回 taobao.com ────►│ │ │ │◄── 淘宝已登录首页 ────────│ │ │ │ │ │ │ │ ... 过了一会儿 ... │ │ │ │ │ │ │ ⑤ │── 访问 tmall.com ──────────────────────────────────►│ │ │ Cookie: _m_h5_tkxxx │ │── 查 Session ──────►│ │ (浏览器自动携带) │ │◄── 用户9527 ────────│ │◄── 天猫已登录首页 ◄──────────────────────────────── │ │ │ │ │ │三句话概括阿里有一个统一登录中心login.taobao.com所有网站共用同一套账号登录成功后登录页面的 JS 通过img标签悄悄向各子站发请求把通行证传过去各子站验证后在自己域下写入登录 Cookie之后你访问任一子站浏览器自动携带该站的 Cookie服务端通过 Cookie 查到你的身份无需再次登录本文基于阿里 SSO 的公开原理进行分析。实际实现中字段名、加密算法、URL 格式等细节可能有所不同但核心架构和原理一致。