session 和 token
session 和 token 都是边界很模糊的概念,就像前面说的,refresh token 也可能以 session 的形式组织维护。狭义上,我们通常认为 session 是「种在 cookie 上、数据存在服务端」的认证方案,token 是「客户端存哪都行、数据存在 token 里」的认证方案。对 session 和 token 的对比本质上是「客户端存 cookie / 存别地儿」、「服务端存数据 / 不存数据」的对比。「客户端存 cookie / 存别地儿」 存 cookie 固然方便不操心,但问题也很明显:
在浏览器端,可以用 cookie(实际上 token 就常用 cookie),但出了浏览器端,没有 cookie 怎么办?
cookie 是浏览器在域下自动携带的,这就容易引发 CSRF 攻击(前端安全系列(二):如何防止CSRF攻击?- 美团技术团队)
存别的地方,可以解决没有 cookie 的场景;通过参数等方式手动带,可以避免 CSRF 攻击。「服务端存数据 / 不存数据」
存数据:请求只需携带 id,可以大幅缩短认证字符串长度,减小请求体积
不存数据:不需要服务端整套的解决方案和分布式处理,降低硬件成本;避免查库带来的验证延迟
单点登录
前面我们已经知道了,在同域下的客户端/服务端认证系统中,通过客户端携带凭证,维持一段时间内的登录状态。但当我们业务线越来越多,就会有更多业务系统分散到不同域名下,就需要「一次登录,全线通用」的能力,叫做「单点登录」。
“虚假”的单点登录(主域名相同)
简单的,如果业务系统都在同一主域名下,比如wenku.baidu.com tieba.baidu.com,就好办了。可以直接把 cookie domain 设置为主域名 baidu.com,百度也就是这么干的。
“真实”的单点登录(主域名不同)
比如滴滴这么潮的公司,同时拥有didichuxing.com xiaojukeji.com didiglobal.com等域名,种 cookie 是完全绕不开的。这要能实现「一次登录,全线通用」,才是真正的单点登录。这种场景下,我们需要独立的认证服务,通常被称为 SSO。「一次「从 A 系统引发登录,到 B 系统不用登录」的完整流程」
用户进入 A 系统,没有登录凭证(ticket),A 系统给他跳到 SSO
SSO 没登录过,也就没有 sso 系统下没有凭证(注意这个和前面 A ticket 是两回事),输入账号密码登录
SSO 账号密码验证成功,通过接口返回做两件事:一是种下 sso 系统下凭证(记录用户在 SSO 登录状态);二是下发一个 ticket
客户端拿到 ticket,保存起来,带着请求系统 A 接口
系统 A 校验 ticket,成功后正常处理业务请求
此时用户第一次进入系统 B,没有登录凭证(ticket),B 系统给他跳到 SSO
SSO 登录过,系统下有凭证,不用再次登录,只需要下发 ticket
客户端拿到 ticket,保存起来,带着请求系统 B 接口
「完整版本:考虑浏览器的场景」 上面的过程看起来没问题,实际上很多 APP 等端上这样就够了。但在浏览器下不见得好用。看这里:
对浏览器来说,SSO 域下返回的数据要怎么存,才能在访问 A 的时候带上?浏览器对跨域有严格限制,cookie、localStorage 等方式都是有域限制的。这就需要也只能由 A 提供 A 域下存储凭证的能力。一般我们是这么做的:
图中我们通过颜色把浏览器当前所处的域名标记出来。注意图中灰底文字说明部分的变化。
在 SSO 域下,SSO 不是通过接口把 ticket 直接返回,而是通过一个带 code 的 URL 重定向到系统 A 的接口上,这个接口通常在 A 向 SSO 注册时约定
浏览器被重定向到 A 域下,带着 code 访问了 A 的 callback 接口,callback 接口通过 code 换取 ticket
这个 code 不同于 ticket,code 是一次性的,暴露在 URL 中,只为了传一下换 ticket,换完就失效
callback 接口拿到 ticket 后,在自己的域下 set cookie 成功
在后续请求中,只需要把 cookie 中的 ticket 解析出来,去 SSO 验证就好
访问 B 系统也是一样
总结
HTTP 是无状态的,为了维持前后请求,需要前端存储标记
cookie 是一种完善的标记方式,通过 HTTP 头或 js 操作,有对应的安全策略,是大多数状态管理方案的基石
session 是一种状态管理方案,前端通过 cookie 存储 id,后端存储数据,但后端要处理分布式问题
token 是另一种状态管理方案,相比于 session 不需要后端存储,数据全部存在前端,解放后端,释放灵活性
token 的编码技术,通常基于 base64,或增加加密算法防篡改,jwt 是一种成熟的编码方案
在复杂系统中,token 可通过 service token、refresh token 的分权,同时满足安全性和用户体验
session 和 token 的对比就是「用不用cookie」和「后端存不存」的对比
单点登录要求不同域下的系统「一次登录,全线通用」,通常由独立的 SSO 系统记录登录状态、下发 ticket,各业务系统配合存储和认证 ticket