鉴权必须了解的 5 个兄弟:cookie、session、token、jwt、单点登录

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了鉴权必须了解的 5 个兄弟:cookie、session、token、jwt、单点登录相关的知识,希望对你有一定的参考价值。

参考技术A

本文你将看到:

**「前端存储」**这就涉及到一发、一存、一带,发好办,登陆接口直接返回给前端,存储就需要前端想办法了。

前端的存储方式有很多。

有,cookie。cookie 也是前端存储的一种,但相比于 localStorage 等其他方式,借助 HTTP 头、浏览器能力,cookie 可以做到前端无感知。一般过程是这样的:

「配置:Domain / Path」

cookie 是要限制::「空间范围」::的,通过 Domain(域)/ Path(路径)两级。

「配置:Expires / Max-Age」

cookie 还可以限制::「时间范围」::,通过 Expires、Max-Age 中的一种。

「配置:Secure / HttpOnly」

cookie 可以限制::「使用方式」::。

**「HTTP 头对 cookie 的读写」**回过头来,HTTP 是如何写入和传递 cookie 及其配置的呢?HTTP 返回的一个 Set-Cookie 头用于向浏览器写入「一条(且只能是一条)」cookie,格式为 cookie 键值 + 配置键值。例如:

那我想一次多 set 几个 cookie 怎么办?多给几个 Set-Cookie 头(一次 HTTP 请求中允许重复)

HTTP 请求的 Cookie 头用于浏览器把符合当前「空间、时间、使用方式」配置的所有 cookie 一并发给服务端。因为由浏览器做了筛选判断,就不需要归还配置内容了,只要发送键值就可以。

**「前端对 cookie 的读写」**前端可以自己创建 cookie,如果服务端创建的 cookie 没加HttpOnly,那恭喜你也可以修改他给的 cookie。调用document.cookie可以创建、修改 cookie,和 HTTP 一样,一次document.cookie能且只能操作一个 cookie。

调用document.cookie也可以读到 cookie,也和 HTTP 一样,能读到所有的非HttpOnly cookie。

现在回想下,你刷卡的时候发生了什么?

这种操作,在前后端鉴权系统中,叫 session。典型的 session 登陆/验证流程:

**「Session 的存储方式」**显然,服务端只是给 cookie 一个 sessionId,而 session 的具体内容(可能包含用户信息、session 状态等),要自己存一下。存储的方式有几种:

「Session 的过期和销毁」**很简单,只要把存储的 session 数据销毁就可以。****「Session 的分布式问题」**通常服务端是集群,而用户请求过来会走一次负载均衡,不一定打到哪台机器上。那一旦用户后续接口请求到的机器和他登录请求的机器不一致,或者登录请求的机器宕机了,session 不就失效了吗?这个问题现在有几种解决方式。

但通常还是采用第一种方式,因为第二种相当于阉割了负载均衡,且仍没有解决「用户请求的机器宕机」的问题。**「node.js 下的 session 处理」**前面的图很清楚了,服务端要实现对 cookie 和 session 的存取,实现起来要做的事还是很多的。在npm中,已经有封装好的中间件,比如 express-session - npm,用法就不贴了。这是它种的 cookie:

express-session - npm 主要实现了:

session 的维护给服务端造成很大困扰,我们必须找地方存放它,又要考虑分布式的问题,甚至要单独为了它启用一套 Redis 集群。有没有更好的办法?

回过头来想想,一个登录场景,也不必往 session 存太多东西,那为什么不直接打包到 cookie 中呢?这样服务端不用存了,每次只要核验 cookie 带的「证件」有效性就可以了,也可以携带一些轻量的信息。这种方式通常被叫做 token。

token 的流程是这样的:

**「客户端 token 的存储方式」 在前面 cookie 说过,cookie 并不是客户端存储凭证的唯一方式。token 因为它的「无状态性」,有效期、使用限制都包在 token 内容里,对 cookie 的管理能力依赖较小,客户端存起来就显得更自由。但 web 应用的主流方式仍是放在 cookie 里,毕竟少操心。 「token 的过期」**那我们如何控制 token 的有效期呢?很简单,把「过期时间」和数据一起塞进去,验证时判断就好。

编码的方式丰俭由人。**「base64」**比如 node 端的 cookie-session - npm 库

默认配置下,当我给他一个 userid,他会存成这样:

这里的 eyJ1c2VyaWQiOiJhIn0=,就是 "userid":"abb” 的 base64 而已。 「防篡改」

是的。所以看情况,如果 token 涉及到敏感权限,就要想办法避免 token 被篡改。解决方案就是给 token 加签名,来识别 token 是否被篡改过。例如在 cookie-session - npm 库中,增加两项配置:

这样会多种一个 .sig cookie,里面的值就是 "userid":"abb” 和 iAmSecret通过加密算法计算出来的,常见的比如HMACSHA256 类 (System.Security.Cryptography) | Microsoft Docs。

好了,现在 cdd 虽然能伪造出eyJ1c2VyaWQiOiJhIn0=,但伪造不出 sig 的内容,因为他不知道 secret。**「JWT」**但上面的做法额外增加了 cookie 数量,数据本身也没有规范的格式,所以 JSON Web Token Introduction - jwt.io 横空出世了。

它是一种成熟的 token 字符串生成方案,包含了我们前面提到的数据、签名。不如直接看一下一个 JWT token 长什么样:

这串东西是怎么生成的呢?看图:

类型、加密算法的选项,以及 JWT 标准数据字段,可以参考 RFC 7519 - JSON Web Token (JWT)node 上同样有相关的库实现:express-jwt - npm koa-jwt - npm

token,作为权限守护者,最重要的就是「安全」。业务接口用来鉴权的 token,我们称之为 access token。越是权限敏感的业务,我们越希望 access token 有效期足够短,以避免被盗用。但过短的有效期会造成 access token 经常过期,过期后怎么办呢?一种办法是,让用户重新登录获取新 token,显然不够友好,要知道有的 access token 过期时间可能只有几分钟。另外一种办法是,再来一个 token,一个专门生成 access token 的 token,我们称为 refresh token。

有了 refresh token 后,几种情况的请求流程变成这样:

如果 refresh token 也过期了,就只能重新登录了。

session 和 token 都是边界很模糊的概念,就像前面说的,refresh token 也可能以 session 的形式组织维护。狭义上,我们通常认为 session 是「种在 cookie 上、数据存在服务端」的认证方案,token 是「客户端存哪都行、数据存在 token 里」的认证方案。对 session 和 token 的对比本质上是「客户端存 cookie / 存别地儿」、「服务端存数据 / 不存数据」的对比。**「客户端存 cookie / 存别地儿」**存 cookie 固然方便不操心,但问题也很明显:

存别的地方,可以解决没有 cookie 的场景;通过参数等方式手动带,可以避免 CSRF 攻击。 「服务端存数据 / 不存数据」

前面我们已经知道了,在同域下的客户端/服务端认证系统中,通过客户端携带凭证,维持一段时间内的登录状态。但当我们业务线越来越多,就会有更多业务系统分散到不同域名下,就需要「一次登录,全线通用」的能力,叫做「单点登录」。

简单的,如果业务系统都在同一主域名下,比如wenku.baidu.com tieba.baidu.com,就好办了。可以直接把 cookie domain 设置为主域名 baidu.com,百度也就是这么干的。

比如滴滴这么潮的公司,同时拥有didichuxing.com xiaojukeji.com didiglobal.com等域名,种 cookie 是完全绕不开的。这要能实现「一次登录,全线通用」,才是真正的单点登录。这种场景下,我们需要独立的认证服务,通常被称为 SSO。 「一次「从 A 系统引发登录,到 B 系统不用登录」的完整流程」

**「完整版本:考虑浏览器的场景」**上面的过程看起来没问题,实际上很多 APP 等端上这样就够了。但在浏览器下不见得好用。看这里:

对浏览器来说,SSO 域下返回的数据要怎么存,才能在访问 A 的时候带上?浏览器对跨域有严格限制,cookie、localStorage 等方式都是有域限制的。这就需要也只能由 A 提供 A 域下存储凭证的能力。一般我们是这么做的:

图中我们通过颜色把浏览器当前所处的域名标记出来。注意图中灰底文字说明部分的变化。

谢谢大家哦

以上是关于鉴权必须了解的 5 个兄弟:cookie、session、token、jwt、单点登录的主要内容,如果未能解决你的问题,请参考以下文章

鉴权必须了解的5个兄弟:cookiesessiontokenjwt单点登录

鉴权必须了解的5个知识点:cookie,session,token,jwt,单点登录

鉴权 5 兄弟:cookiesessiontokenjwt单点登录,终于有人说清楚了。。。

前端鉴权的兄弟们:cookiesessiontokenjwt单点登录

接口的鉴权cookiesession和token

2Python全栈之路系列之Tornado的Cookie与Sess