likes
comments
collection
share

你登录了吗?- 聊一聊 cookie , session , token

作者站长头像
站长
· 阅读数 34

🌀前置小知识

🌈HTTP 是无状态的

HTTP (Hyper Text Transfer Protocol)是一种无状态的网络数据交换协议!专业点叫 《超文本传输协议》

它就像一条鱼 🐟 , 仅在一次连接时 ,有记忆 , 能够对当前不同的连接进行区分!

但连接断开后,再次连接,它就会忘记你是谁了!相当于重新认识!

你登录了吗?- 聊一聊 cookie , session , token

之所以如此是因为在 http 设计之初 ,就没有这个需求和必要!当时的功能很简单,就是一台电脑使用 http 协议 ,与另一台电脑进行连接后 ,可以访问到另一台电脑里的 html 文件!

因此, 在当时, 鱼就够用了!

然而 ,这一切仅仅是开始!在之后的发展中 , 仅仅预览或下载 html 文件是远远满足不了人们的需求了!因此 http 在之后的发展中 ,http 可以进行其他数据的交流 ,比如图片 , 视频 ,脚本文件等等!

但是 ,由于 http 的特性 , 始终是无法解决无状态的问题的!

❓为什么需要状态?

如今的网站 ,大多数是需要用户注册的 ,也就是它需要区分不同用户!假设一种情况 ,你登录了某个网站 , 但是这个登录状态仅在你发出的登录请求这个过程中有效 ,那么在需要登录后才能进行的请求 ,你可能需要每次都要携带上账号密码!

对此们可以在登录后返回用户一个登录的凭证 ,比如 JS 存放在全局变量中 ,每次请求带上即可!然而这还有一个问题 ,就是如果你关闭了游览器或者关闭标签页,或者刷新一下 ,数据就丢失了,你又需要重新登录!

也就是 , 不同的连接之间 ,没有联系 ,没有状态 ,服务器需要游览器在这些连接之间携带身份信息,或者身份凭证 ,从而辨别身份!

🍪Cookie

Cookie 是一个保存在客户机中的简单的文本文件 。它的出现解决了 http 无状态特性所带来的问题!

Cookie 所记录的数据与文档关联,可以保存用户在该文档上的一些操作记录和数据!比如登录状态!比如用户的身份信息等!

🥔Cookie 的组成

Cookie 本质来说,是文本数据。形式上是 键值对 。 键值对之间通过 ; 分隔!

通过 document.cookie 可以访问和设置 cookie !

比如

document.cookie='h_key=h_value'

在游览器的效果就是:

你登录了吗?- 聊一聊 cookie , session , token

从上图可以发现 ,Cookie 不仅仅只有一个 ,单个 Cookie 除了名称和值 ,还有其他属性!比如 Domain , Path , Expires 等等!

比如 :

h_key=h_value; expires=Mon, 22-Jan-07 07:10:24 GMT; domain=.abc``.com;path=/;Secure;HttpOnly

但实际发送的只有 cookie 的名/值对。也就是名称和值,其他是告诉游览器一些 cookie 的信息的,如该不该发送,什么时候删除等

cookie 在浏览器中是由以下属性构成的:

name (名称)

唯一标识 cookie 的名称。cookie 名不区分大小写,但实践时最好区别大小写,因为可能有的服务器软件区别。cookie 名必须经过 URL 编码。

它可以用于表示 cookie 的作用和含义 ,比如记录账号的 ,就可以叫 username 。

value (值)

存储在 cookie 里的字符串值。于 name 组成键值对 ,就是一个 cookie 的基本信息。 这个值也必须经过 URL 编码。

Domain (域)

cookie 的有效域。也就是什么域可以使用这个 cookie 。 发送到这个域的所有请求默认都会包含对应的 cookie。这个值可能包含子域,也可能不包含。如果不明确设置,默认为设置使得域名

Path (路径)

请求 URL 中包含这个路径才会把 cookie 发送到服务器。也就是指定的路径才会在发送请求时把 cookie 包含进去。 默认值是设置 cookie 使得路径

Expires (过期时间)

表示何时删除 cookie 的 时间戳(即什么时间之后就删除)。

默认情况下(没有设置 Expires),浏览器会话结束后(关闭游览器)会删除该 cookie。

如果设置了时间,值是 GMT 格式(Wdy, DD-Mon-YYYY HH:MM:SS GMT),用于指定删除 cookie 的具体时间。在有效时间内即使关闭游览器也不会被删除。当过期后,游览器会删除该 cookie

Secure (安全标志)

设置之后,只在使用 SSL 安全连接 (https 请求)的情况下才会把 cookie 发送到服务器 。 使用安全标志只要在 cookie 中增加 Secure 即可。

比如 document.cookie=k=v;Secure

HttpOnly

只能通过后台设置 ,设置后, JS 无法对 cookie 进行读取和修改 。 仅游览器可以对其操作。

🔧Cookie 的设置

document.cookie可以设置和访问非 httponly 的 cookie 。

比如:

document.cookie = `h_key=h_value; expires=${new Date()};domain=127.0.0.1;path=/;secure;

设置非存在的过期的 cookie , 不会被设置上去

每一次给执行 document.cookie ,都会生成新的 cookie (非覆盖,非删除操作时)。

比如:

document.cookie='h_key=h_value'
document.cookie='h_key1=h_value1'
document.cookie='h_key2=h_value2'

你登录了吗?- 聊一聊 cookie , session , token

不同的 cookie 通过 name 标识 ,不同的 name , 就会生成新的 cookie !

当两次以上的设置 同 name 的 cookie 时 ,会覆盖之前的 cookie 。

🪚Cookie 的删除

不同于 Storage 有 remoteItem 接口可以指定删除某个数据 。 Cookie 的删除,必须通过覆盖!

也就是重新设置 document.cookie 。

Cookie 是否删除取决于 expires , 也就是过期时间!当设置一个过去的时间, 0 或者负数时,游览器在发送请求时,就不会带上改 cookie ,并且会删除改 cookie !

删除 cookie 的几种情况

  • 设置成过去的时间

设置成过去的时间,比如 new Date(Date.now() - 8 * 3600 * 1000) 设置成一个过期的时间 , cookie 马上删除!

需要注意一点 ,游览器看是否过期是根据 GTM 时间 ,实际来说 ,通过 new Date() 获取的时间是 UTC 时间 。与 GTM 时间仅有一秒内的差距 ,不过问题在于 , new Date 默认获取到的时间是在 GMT 的时间的基础上加上区时差。也就是 ,我们获取到的是北京时间(东八区) ,比 GMT 快 8个小时 。

  • 设置成 0 或者 负数

🦖Session

📞什么是 session

Session 也叫做会话!是服务器为了保存用户状态而创建的一个特殊的对象。

用于存储服务端与客户端之间的会话信息等。可以理解为存储在后台的 cookie 。

当客户端于服务端第一次建立连接后 ,服务端会创建一个 session 对象 。此对象有一个唯一的 id 。 一般是 sessionId 。 这个 sessionId 会以 cookie 的形式发送给客户端 ,客户端保存这个 id , 在之后的通信中带上这个 id , 服务器通过 Id 就可以找到对应的 session , 就可以对客户端进行身份识别并获取一些会话记录!

你登录了吗?- 聊一聊 cookie , session , token

为什么说 , session 就像保存在 后台的 cookie 呢! 因为他们的功能其实是一样的 。目的就是记录会话信息 。比如 cookie 可以保存用户名 ,用户 id , 用户游览记录等等! Session 的目的也是如此。

不过 session 是保存的服务端的 ,因此要区分不同游览器对应不同的 session , 需要给游览器一个 sessionId , sessionId 是以 cookie 的形式传递给游览器的!

Session 基本上依赖于 cookie , 但也可以不依赖于 cookie 。 比如 URL重写 ,即将 cookie 放在 URL中 ,不过这相比于 cookie 更麻烦且更不安全了!

❓为什么使用 session

如此看来, 倒不如直接使用 cookie ! 那为什么还需要 session 呢?

之所以还需要 session , 主要有以下这些原因:

  • Cookie 大小有限制

Cookie 被限制在 4k 以内 ,单个 cookie 的大小也有限制 ,数量也有限制 。因此无法通过 cookie 保存过多的信息!

  • Cookie 本身不安全

Cookie 容易被劫持和伪造。如果在 cookie 中保存了用户的一些信息,那么很容易被劫持后盗取信息 。另外伪造一个 cookie 也相对容易,那么当安全性较低时 ,用户身份就很可能被盗用!或者伪造一个身份向服务器发送请求。

  • Cookie 数据会加大传输压力

对应网站的所有未过期的 cookie 在每次请求时都会带上,如果在 cookie 中存大量数据,但是其实用到的 ,只有用于身份验证的 cookie , 那么会导致传输压力变大!

而Session 是为什么没有这些问题的呢?

  • Session 保存在后台 ,本身就没有大小的限制。因此你可以存放很多信息
  • Session 只需要给游览器一个 sessionId , 这个 sessionId 可以加密 ,不易伪造和破译。相对安全很多。并且将信息存放在后台 ,即使 cookie 被他人获取 ,也难以通过 cookie 就获取用户信息。
  • 因为数据都存储在后台 ,那么就不需要传输来传输去 ,那么就减少了请求中请求头的大小。

🧨Sesssion 的问题

当一个服务的业务越来越大 ,访问量越来越多 。为了持续稳定的提供服务 ,服务器可以考虑从垂直扩展和水平扩展两个方面扩展负载能力。

  • 垂直扩展

垂直扩展即加强服务器本身的负载能力 ,提高加强硬件处理能力 。比如 CUP 处理能力 , 储存大小等等 , 甚至从软件上也可以择优使用!但这始终有瓶颈!

  • 水平扩展

水平扩展即从一台服务器扩展到多台服务器去提高服务!从而将负载分担到不同服务器中 。

  • 水平扩展有两种方法:
    • 分布式服务器:一个服务分成多个子服务,不同服务器服务不同需求
    • 集群服务器 (负载均衡): 不同服务器提供相同的服务,服务相同的需求

(简而言之,分布式就是不同人干不同活,你负责生产,我负责销售。集群就是自己干自己的,即生产,也销售,但是多个人一起干)

然而 ,无论是分布式还是集群 ,如果使用了 session 就会有一个问题 ,就是 session 可能在服务器 A 中创建 , 但是下一个请求,可能访问了服务器 B 。 但是服务器 B没有这个 session , 也就是对服务器 B 来说 ,此时的请求,它是不认识的!

而使用 cookie 记录会话信息就不会有这个问题 。因为 cookie 保存在游览器 ,无论访问服务器A 还是 服务器 B 都会带上!

🪠问题解决

那如何解决水平扩展后 session 的问题呢?

可以将解决方法分为三类:

  • Session 保持
  • Session 复制
  • Session 共享

Session 保持

仅适用于集群服务器 ,即负载均衡 。 其原理就是将同一个客户端的请求发到同一个服务器上 。使客户端始终与同一个服务器进行会话。

这个方法实现简单 ,只需要设置适当的负载均衡算法即可!

不过问题在于可能导致一个服务器接受过多的请求(这与 负载均衡 算法有关)。如果该服务器宕机了, 会话就会丢失!

Session 复制

Session 复制是在一个服务器创建了 session 后 ,把 session 复制到其他服务器 ,并且在之后保持 session 的同步!那么无论请求发到哪个服务器 ,都能保持会话!

你登录了吗?- 聊一聊 cookie , session , token

Session 共享

Session 共享是将 session 集中在一个数据库中保存 。不同的服务器接受到请求后 ,通过 sessionId 向服务器获取 session 即可!

你登录了吗?- 聊一聊 cookie , session , token

🗝️Token

🔑什么是 Token

Token 叫做令牌 。是用户在服务器登录后 ,服务器颁发给服务器一个令牌 。这个令牌保存了用户的一些基本信息 ,比如用户名称 ,用户权限等,也需要保存一些特有的数据 ,比如签名 (sign),过期时间等!

Token 都会经过加密后才发给游览器 ,这在一定程度上保证了数据不会轻易泄露!

应用在接收到 token 后缓存起来,在之后的请求时带上 , 服务器接收到 token 后解密 token , 从而可以识别用户身份!

你登录了吗?- 聊一聊 cookie , session , token

❓为什么使用 Token

为什么使用 token , 主要有以下几点原因:

  1. Token 完全由应用管理,所以它可以避开同源策略
  1. Token 可以避免 CSRF 攻击
  1. Token 可以是无状态的,可以在多个服务间共享

这些优点怎么理解和体现,就需要对比 cookie 和 session 的缺点了:

  • Cookie 不安全

Cookie 容易被跨站攻击。也就是 CSRF 攻击 。因为游览器在请求时会默认带上 Cookie 。而 Token 是完全由应用管理 ,也就是带不带 ,由我们决定!

  • Cookie 有跨域限制

Cookie 的 domain 和 path 属性可以限制 cookie 应该在哪些请求中带上 cookie 。但如果有跨域的请求 , 我们希望带上 cookie , 但游览器不允许!而 token 由我们控制,就可以带上 ,不受同源策略限制!

  • 大量的 Session 会给造成服务器巨大压力

因为 session 是保存在服务器端的,如果访问量过多,就会给服务器造成过大的压力!使得响应变慢。而 token 是保存在客户端的 ,缓解了服务器的压力

  • Session 不太适合分布式和集群

Token 可以是无状态的(服务器不知道它是否已经失效) 。也就是用户登录后把用户信息保存在 Token 中 , 这样子客户端请求其他服务器时只需要带上 token , 服务器解析 token ,就能获取身份 。

🪧JWT

JWT (Json Web Token) 是 一种轻量级的认证规范 ,本质是一种 token !

Token 分为有状态和无状态!

JWT 就是一种状态的 token ! 所谓无状态是指在 token 的过期时间到达之前,服务器无法主动让其失效。同时,服务器也不知道它是否应该失效!

相当于你给别人你家的密码 ,你一个星期换一次密码 ,但一个星期内,你不知道密码是否已经泄露。因此这个时候 ,只要有密码就能进你家 ,被偷了东西,你还不知道!

因此对于 JWT 的使用需要十分谨慎!一般很少使用!

不过 JWT 也有它的特点,就是可以去中心化 。也就是在集群和分布式中 ,一个 token , 可以到处访问!还有实现单点登录!也超级的方便简单!

🎐有状态 Token

有状态 Token 其实也是无状态 Token, 它的有状态是因为后台也保存了 Token 。 这其实就和 session 差不多了 。只不过造成的负担会小一些!

之所以使用有状态的 Token ,是因为无状态的 Token (比如 JWT ) 有一定的风险 ,如果 token 泄露,那么服务器没办法知道这个 token 是不是被盗用的。也没有办法让这个 token 失效!

而有状态 Token ,可以在用户登出,或者服务器在发现该 Token 的请求有一定的问题后 ,让该 Token 失效。从而降低风险!

让 Token 失效的方法,一般有两种:

  • 白名单

白名单是将生成有效的 token 缓存起来 ,在验证 Token 时 ,先验证是否有该 Token ,没有即失效 ,有就解密该 Token ,获取用户身份信息 。 如果需要一个 Token 失效 ,在白名单中删除该 Token 即可!

  • 黑名单

黑名单即将失效的 Token 储存起来 ,请求的 Token 需要在黑名单中查找 ,如果有这个Token ,则说明已经失效 , 没有则有效!在用户登出后,即可将该 Token 存储在黑名单中,这里需要注意一个点 ,就是黑名单保存 Token 的时间需要比 Token 本身的有效时间长 。