储存状态的方式
小明的故事说完了,该来把上面这一段变成网络的实际案例了。其实在网络世界中问题也是一样的。
前面已经提到过我们会把状态存在 Cookie 里面,让 Request 之间能够变得有关联。
假设我们今天要来做一个会员系统,那我要怎么知道这个 Request 代表的是哪一个会员?
最直觉的方式就是登入以后把会员帐号存在 Cookie 里面嘛,这样不就知道是谁了吗?
可是会碰到的问题就跟寄杯的故事一样,Cookie 里的东西是可以被窜改的,如果我改成了别人的会员帐号,我就可以伪造他的身份登入了!
解决方法跟上面寄杯的解法一样:
第一个解法就是把 Cookie 里面的内容给加密,这样就无法被窜改了。这种方式就称之为 Cookie-based session,意思就是你把所有的 Session 状态都存在 Cookie 里面。
所以不要把「用 Cookie 来操作 Session 机制」跟「Cookie-based session」搞混了,两者是不一样的。
至于缺点的话前面有提到,Cookie 的大小是有限制的,超过大小的话浏览器就不帮你存了。
因此当你想存的信息越来越多,Cookie 当然也越来越大,就有可能超过这个限制。
或者是哪天你的加密方式以及密钥被黑客破解,那黑客一样可以伪造任何人的身份。
第二个解法就是透过一个 ID 来辨识身份,这个 ID 称之为 Session Identifier,简称 Session ID。
Server 只在 Cookie 里面存一个 Session ID,其余的状态都存在 Server 那边,我习惯把 Server 那边的数据称为 Session Data:
其实就是小明笔记本的翻版而已
Session ID 的产生方式跟前面说的一样,通常会是一个无法猜测的随机数。
你可能会想说:「很难猜是一回事,但机率不是 0 啊!」,对,的确是有机率能够猜到,但是那个机率太低太低了(例如说几千亿分之一之类的)。
而且 Server 在你乱猜猜错几次之后就有可能把你 ban 掉不让你继续猜,所以没什么问题。
不过这边要特别注意的一点是 Session ID 基本上是种认证不认人的方式,也就是说一旦你的 Session ID 被偷走,别人就可以伪造你的身份来登录了。而这个 Session ID 通常都是保存在 Cookie 之中。
这就是为什么有些网站发生骇客入侵的情形之后你会突然被注销,因为黑客可能偷到一批 Session ID,这时候服务器就会把所有 Session 数据全部清空。
以故事来比喻就是把笔记本丢掉,买一本新的,这样被偷走的那些 Session ID 就没用了,而 Server 找不到你的 Session ID,自然就无法登入,因此把你给注销了。
网站发生问题时客服会要你先把 Cookie 清掉也是类似的道理,因为 Cookie 跟状态有关,有时候可能程序有一些 Bug,把你导到了错误的状态,把 Cookie 清空等于把状态清空,重新再开始,就有可能变得正常。
总结
其实我原本以为我很懂 Cookie 跟 Session,但越研究越发现好像不是这么一回事,只是我自我感觉良好而已。
但把该看的数据都看完一遍之后,再让自己沉淀个几天,大致上就能完全理解整个脉络的发展。
Session 是什么?就是一种让 Request 变成 Stateful 的机制。以小明的例子来说,Session 就是一种让客人之间能互相关联起来的机制。
在故事里面我们用了纸条跟手机里的信息来比喻,有多种方式可以达成 Session。
在网络世界中,也有很多种方式可以来实作 Session,前面介绍过第一种是地址栏,第二种就是靠 Cookie,而 Cookie 就是存在浏览器里的一些信息。
常见的错误认知是一定要有 Cookie 才能实作 Session,这是错误的。
有了 Session 之后,会碰到数据被窜改的问题,这时候有两种解决方式:
-
一个是 Cookie-based session,意思是你照旧把状态存在 Cookie,但是加密以后再存。
-
另一个方法是把状态存在 Server 端,靠一个 Session ID 来辨识,这个状态你可以存成档案,可以存在內存里,也可以存在数据库,只是实作方式的不同而已,但原理都是一样的。
而这个状态储存的地方在口语上也会被称之为「Session」,例如说:「帮我把 user id 存在 Session 里」,或者是「注销记得把 Session 清空」之类的。
所以在实际用法中,我认为 Session 之所以不好理解是因为太多地方用到同一个词,但却是在指代不同的东西(可是又很类似)。跟 API 有点像,太多地方都用到这个词了。