根本原因

http 是无状态的协议

以后能从这根本上解决吗?

1.很久以前:没有这个需求

很久很久以前,Web 基本上就是文档的浏览而已,既然是浏览,作为服务器,不需要记录谁在某一段时间里都浏览了什么文档,每次请求都是一个新的 HTTP 协议, 就是请求加响应, 尤其是我不用记住是谁刚刚发了 HTTP 请求, 每个请求对我来说都是全新的。这段时间很嗨皮

2. 社会发展,需求发现,问题出现——新技术出现解决:session

但是随着交互式 Web 应用的兴起,像在线购物网站,需要登录的网站等等,马上就面临一个问题,那就是要管理会话,必须记住哪些人登录系统, 哪些人往自己的购物车中放商品, 也就是说我必须把每个人区分开,这就是一个不小的挑战,因为 HTTP 请求是无状态的,所以想出的办法就是给大家发一个会话标识(session id), 说白了就是一个随机的字串,每个人收到的都不一样, 每次大家向我发起 HTTP 请求的时候,把这个字符串给一并捎过来, 这样我就能区分开谁是谁了

3、带来的新问题:服务器压力

开始上网人不多,不断的社会发展带来了新的问题:

这样大家很嗨皮了,可是服务器就不嗨皮了,每个人只需要保存自己的 session id,而服务器要保存所有人的 session id ! 如果访问服务器多了, 就得由成千上万,甚至几十万个。

这对服务器说是一个巨大的开销,严重的限制了服务器扩展能力, 比如说我用两个机器组成了一个集群,小 F 通过机器 A 登录了系统, 那 session id 会保存在机器 A 上, 假设小 F 的下一次请求被转发到机器 B 怎么办? 机器 B 可没有小 F 的 session id 啊。

3.1 集群出现,解决服务器压力——但带来新的问题:session 迁移损耗

有时候会采用一点小伎俩: session sticky , 就是让小 F 的请求一直粘连在机器 A 上, 但是这也不管用, 要是机器 A 挂掉了, 还得转到机器 B 去。那只好做 session 的复制了, 把 session id 在两个机器之间搬来搬去, 快累死了。

3.2 集中存储 session——新问题:不能高可用,易崩溃

后来有个叫 Memcached 的支了招: 把 session id 集中存储到一个地方, 所有的机器都来访问这个地方的数据, 这样一来,就不用复制了, 但是增加了单点失败的可能性, 要是那个负责 session 的机器挂了, 所有人都得重新登录一遍, 估计得被人骂死。

也尝试把这个单点的机器也搞出集群,增加可靠性, 但不管如何, 这小小的 session 对我来说是一个沉重的负担

4. 跳出 session 新技术解决:token——抓住核心 验证

于是有人就一直在思考, 我为什么要保存这可恶的 session 呢, 只让每个客户端去保存该多好?

可是如果不保存这些 session id , 怎么验证客户端发给我的 session id 的确是我生成的呢? 如果不去验证,我们都不知道他们是不是合法登录的用户, 那些不怀好意的家伙们就可以伪造 session id , 为所欲为了。

嗯,对了,关键点就是验证 !

比如说, 小 F 已经登录了系统, 我给他发一个令牌(token), 里边包含了小 F 的 user id, 下一次小 F 再次通过 Http 请求访问我的时候, 把这个 token 通过 Http header 带过来不就可以了。

不过这和 session id 没有本质区别啊, 任何人都可以可以伪造, 所以我得想点儿办法, 让别人伪造不了。

那就对数据做一个签名吧, 比如说我用 HMAC-SHA256 算法,加上一个只有我才知道的密钥, 对数据做一个签名, 把这个签名和数据一起作为 token, 由于密钥别人不知道, 就无法伪造 token 了。

这个 token 我不保存, 当小 F 把这个 token 给我发过来的时候,我再用同样的 HMAC-SHA256 算法和同样的密钥,对数据再计算一次签名, 和 token 中的签名做个比较, 如果相同, 我就知道小 F 已经登录过了,并且可以直接取到小 F 的 user id , 如果不相同, 数据部分肯定被人篡改过, 我就告诉发送者: 对不起,没有认证。

Token 中的数据是明文保存的(虽然我会用 Base64 做下编码, 但那不是加密), 还是可以被别人看到的, 所以我不能在其中保存像密码这样的敏感信息。

当然, 如果一个人的 token 被别人偷走了, 那我也没办法, 我也会认为小偷就是合法用户, 这其实和一个人的 session id 被别人偷走是一样的。

这样一来, 我就不保存 session id 了, 我只是生成 token , 然后验证 token , 我用我的 CPU 计算时间获取了我的 session 存储空间 !

解除了 session id 这个负担, 可以说是无事一身轻, 我的机器集群现在可以轻松地做水平扩展, 用户访问量增大, 直接加机器就行。 这种无状态的感觉实在是太好了!