花了三小时,总算搞懂了Cookie、Session、Token

72 阅读6分钟

从刚开始学习web到今天,对这三者的理解总是模模糊糊,似懂非懂,印象只停留在像大多数人讲到的cookie存在客户端、session存在服务端、token适用于分布式环境。然而这些表述都过于简单,所以趁今日得闲整理下,如果有什么错误或者遗漏请指出。

先给结论

cookie是一种存储技术;session、token本质上都是一种签名方式;jwt是token的一种具体实现;session常常依赖于cookie实现;token可以选择基于cookie实现也可以选择直接放在http请求头部中;所以cookie和session、token不是一个层面的东西。

前置知识

阅读开始前,首先交代一些阅读文章时可能需要知道的前置知识。

首先,在https普及的今天,黑客想通过“拦截”的手段(会话劫持)获取你的通话信息几乎是不可能,但还是存在有黑客能够通过使用流氓软件的形式或者CSRF手段盗取你浏览器的cookie,从而登录你的网站账号,所以如果是对安全性要求高的服务一般都会要求你进行二次验证,虽说不至于盗取你的支付信息,但是拿你的b站账号乱发东西还是没问题的。

所以有一些前提,用户有义务保证自己的密码安全,不告知他人;不要裸奔或安装来路不明的软件;同样也有义务保证自己的设备安全,及时锁屏等。现在浏览器设置里面查看自动保存的密码,是需要输入开机密码的,建议浏览器在打开控制台功能时,也要输入密码。

开始正文

先聊聊“cookie存在客户端、session存在服务器端、token适用于分布式环境”这一说法。它是正确但浮于表面的认知。

当token使用cookie的方式存储,那它是cookie呢还是token呢?当我把token又存本地又存服务器,那他是session还是token呢?读完下文应该可以解答这几个问题了。

Cookie: 是浏览器中的一个存储技术,指浏览器中保存数据的标准方法,每个浏览器都要实现。当一个用户浏览特定网站的时候,开发者可以通过调用标准的函数设置本地的cookie值,并且每次发送请求时,都默认附加这个cookie请求。cookie仅仅只是一门存储技术。

Session:session能做的,通过程序员自己造轮子用cookie也可以实现,。只不过每个服务都需要保存用户状态,与其在服务器端各造各的轮子,还不如直接搞一个简单的写法,程序员按照这个写法写,服务器帮忙完成生成密钥,识别用户这类繁琐工作。但是session方便是方便,但是时效性太短了,比如tomcat的session默认失效时间是用户停止活动后的20分钟,等于吃个饭回来,就要重新登录了。而且session的默认时间取决于服务器管理员的统一设置,写网页的程序员没法控制,不太灵活,所以有些场景不适用。

Token:既然cookie存本地不安全,session时效太短且受到服务器设置的影响,那程序员想按自己的方式授权怎么办,那就生成一个token给用户好了,用户你凭借这个token就可以让服务器识别身份,每次访问网页提交token,由写网页的程序员想办法去验证是否是这个用户。这样程序员想通过这个token绑定什么权限,设定多久过期,权利都在程序员手中,更为灵活。另外token可以实现分布式的鉴权。

照应前文:cookie是一种存储技术;session、token本质上都是一种签名方式;jwt是token的一种具体实现;session常常依赖于cookie实现;token可以选择基于cookie实现也可以选择直接放在http请求头部中;所以cookie和session、token不是一个层面的东西。

至于为什么会被当成是同一层次,我有这样的猜测。

起初的身份认证时代:服务器获取请求中的用户名和密码,判断和数据库的数据是否一致,如果数据一致,就将用户信息回写到cookie到浏览器(也就是常说cookie存在客户端),跳转到主页。如果数据不一致就响应用户或密码错误。

起初的保持登录状态:浏览器请求其他网页时,服务器判断是否有cookie数据,如果有就证明用户是在线的(这种设计确实太过于简单了),如果没有,那么证明用户不在线,需要重新登陆。

可能是为了方便称呼这一时期的认证方式,有人就简述为了cookie时代。

session的身份认证时代:获取用户数据和数据库数据判断,如果是登陆操作,用户数据和数据库数据相同,则开辟session,并将数据(用户信息等)存入session域(也就是常说session存在服务端),回写sessionID,跳转到主页。如果不相同,就响应用户和密码错误。

session的保持登陆状态:浏览器访问其他页面,判断sessionID是否一致,如果id一致就证明用户是在线的,去获取session域里的数据再去拿数据库数据,将数据显示到页面上。如果id不一致,用户并不在线,需要重新登录。在服务端创建session,一般会把登录用户信息以sessionId为key,用户信息为value保存在服务器内存,下次请求进来,根据请求里带的sessionId去查询,如果查询得到用户信息,则表明你是用了这个用户登录,则放行,假如查询不到,则跳到登录页面要你重新登录

token时代:token是无状态的,他只是一个身份凭证。 如果你没有在服务端保存登录状态则无法控制jwt的有效性。在无状态登录模式下,也许你只能通过更改jwt的生成密匙才可以使以前的jwt失效;而我们可以用redis保存登陆状态并设置过期时间。

jwt是token的一个具体实现,由于笔者很少使用就不弄拙了,给出参考文章。

JWT 身份认证优缺点分析

而我常用的设计token的思想:登陆成功分发token,并把token=>uid的形式存在redis设置过期时间,返然后回token给前端->前端的请求携带着token,去查redis如果存在id说明已登陆,放行,然后刷新token时间。

文末

最后,再怎么样会话信息也有被截取的可能,以上措施只是增加截取成本而已。关键在于最大的目的就是保持登陆状态,减少用户操作;但保密性很强的内容还是要靠用户名密码来验证,比如支付,私密空间等,必须输入密码进行二次认证,所以站在用户角度,请一定妥善做好自己的信息安全保护哦。