session、cookie、token

242 阅读10分钟

cookie

cookie 是一种在客户端存储数据的技术,它是由服务器发送给客户端的小型文本文件,用来记录用户的一些信息,存储在客户端的浏览器中,大小限制大致在 4KB 左右。

cookie 主要用于以下三个方面:

  1. 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
  2. 个性化设置(如用户自定义设置、主题等)
  3. 浏览器行为跟踪(如跟踪分析用户行为等)

cookie 主要特点:

  1. cookie 存储在客户端
  2. cookie 不可跨域,但是在如果设置了 domain,那么它们是可以在一级域名和二级域名之间共享的

cookie虽然可以用来存储各种数据,但随着更多浏览器存储方案的出现,cookie存储数据这种方式逐渐被取代,主要原因有如下:

  1. cookie 有存储大小限制,4KB 左右
  2. 浏览器每次请求会携带cookie在请求头中
  3. 字符编码为 Unicode,不支持直接存储中文
  4. 数据可以被轻易查看

cookie的主要属性

属性名描述
namecookie 的名称
valuecookie 的值
commentcookie 的描述信息
domain可以访问该 cookie 的域名
expirescookie 的过期时间,具体某一时间
maxAgecookie 的过期时间
pathcookie 的使用路径
securecookie 是否使用安全协议传输,比如 SSL 等
versioncookie 使用的版本号
isHttpOnly指定该 cookie 无法通过 JavaScript 脚本拿到,比如 Document.cookie 属性、XMLHttpRequest 对象和 Request API 都拿不到该属性。这样就防止了该 cookie 被脚本读到,只有浏览器发出 HTTP 请求时,才会带上该 cookie

流程

客户端向服务端发送请求时,服务端需要记录该用户的状态和一些信息,服务端就会设置响应头的Set-Cookie字段信息来通知客户端保存cookie。当客户端再向服务端发起请求时,客户端会自动在请求头中将相应的cookie信息发送给服务端,服务端接通过读取客户端发送过来的cookie信息,就可以判断是来自哪个客户端发送过来的请求,以此来辨认用户状态。

image.png

第一次客户端发起请求服务端返回的响应信息如下:响应头Response HeadersSet-Cookie的字段信息来通知客户端保存Cookie。

image.png

当客户端再次发送请求到服务端时,就携带了cookie信息在请求头Request Headers中一同发送给了服务端:

image.png

通过 cookie 来实现用户确定或者权限确定流程图如下:

image.png

  1. 客户端发送请求到服务端(比如登录请求)
  2. 服务端收到请求后生成一个 session 会话
  3. 服务端响应客户端,并在响应头中设置Set-Cookie字段。Set-Cookie字段里面包含了 sessionId,它的格式如下:Set-Cookie: value; expires=date;domain=domain;path=path;secure。其中 sessionId就是用来标识客户端的
  4. 客户端收到该请求后,如果服务端响应头设置了Set-Cookie字段,那么客户端发送其它请求就会在请求头中自动携带 cookie,cookie 中携带有用户信息等
  5. 服务端接收到请求,验证 cookie 信息,比如通过 sessionId 来判断是否存在会话,存在则正常响应

session

session在网络应用中称为会话控制,是服务器为了保存用户状态而创建的一个特殊的对象。session就是一个存储于服务器端的对象,用于存储信息

服务器会为每一个浏览器(客户端)创建一个唯一的session,这个session是服务器端共享,每个浏览器(客户端)独享。可以在session对象中存储数据,实现数据共享。

image.png

session的存储形式

session类似于一个Map字典,里面可以存放多个键值对,是以key-value键值对的形式进行存放的。key必须是一个字符串,value是一个对象。

session底层实现机制

session是每一个浏览器(客户端)所唯一的,其实现机制是:HTTP请求的请求头中可以携带cookie,这个cookie包含了sessionId,这个sessionId表示的就是session所对应的sessionId,这个是由服务端创建的,并且是唯一的。服务端在使用session时,会根据sessionId来进行不同操作。

  1. 客户端(浏览器)发送首次请求到服务端
  2. 服务端收到请求,发现客户端没有提供会话ID(缺少一个值为 sessionId 的cookie)
  3. 服务端创建一个新的会话,生成一个唯一的会话ID,并将创建的 sessionId 存储在cookie 中返回给客户端,以及将会话数据保存在服务端存储的session对象中(可以将session对象存储在内存、数据库等)
  4. 服务端将会话ID放在响应头的 Set-Cookie 字段中发送回客户端,客户端浏览器会存储这个会话ID。
  5. 自此,客户端的每个请求都会附带这个会话ID的 sessionId cookie。
  6. 服务器根据收到的会话ID,从服务端的存储中查找相应的会话数据,以恢复用户的会话状态

通常情况下,cookie 和 session 都是结合着来用,服务器端将 session 的唯一标识 sessionId 通过设置响应头Set-Cookie 的方式响应给客户端,客户端将 sessionId 存到 cookie 中。当然可以单独只使用 cookie 或者单独只使用 session,这里将 cookie 和 session 结合着来用,如下图:

image.png

使用express框架管理session(会话)

需要使用express-session中间件,可以使用下面的命令安装这个模块:

npm i express-session

举例:使用session记录页面访问次数

import express from 'express';
import session from 'express-session';
// 引入中间件
import loggerMiddleware from './middleware/logger.js'
// express是函数
const app = express();
// 注册中间件,需要在注册路由模块前先执行,否则路由请求不会经过该中间件
app.use(loggerMiddleware);
// 使用express-session中间件
app.use(session({
    secret:'secret_Key', // 密钥,用来签名sessionId
    name: "sessionName", // 修改session对应的cookie的名称
    resave:false, // 会话在请求过程中未被修改过就不保存
    saveUninitialized: false, // 未初始化的session不存储
}));
// 中间件初始化静态资源,传入静态资源目录名,如此该目录下的文件就会作为静态资源
// 访问资源时加上前缀
app.use('/assets',express.static('static'));
app.get('/',(req,res) => {
    if(req.session.views){
        // req.session的views属性存在,表示views的session对象存在
        req.session.views++;
        res.json({
            code:200,
            data:`第${req.session.views}次访问页面`
        });
    }else {
        // req.session的views属性不存在,第一次访问页面,访问页面次数为1
        req.session.views = 1;
        res.json({
            code:200,
            data:`第一次访问页面`
        });
    }
});
app.listen(8089, () => {
    console.log("8089端口已启动");
});

image.png

image.png

当客户端第一次请求时,服务端给req.session写入一个views属性并在响应头返回保存了该属性的session对象的cookie给客户端。当客户端再次发起请求时会在请求头携带保存了sessionId的cookie给服务端,服务端把req.session.views加1,然后返回给客户端。客户端再次请求之后会响应用户访问的次数。

session与cookie的区别

cookie 和 session,它们两者之间主要是通过 sessionId 关联起来的,所以sessionId 是 cookie 和 session 之间的桥梁。

  1. 作用范围不同:cookie 保存在客户端(浏览器),session 保存在服务端。
  2. 存取方式不同:cookie 保存中文属于Unicode字符,在内存中占4个字节,而英文属于ASCII字符,内存中只占2个字节。cookie中使用Unicode字符时需要对Unicode字符进行编码,否则会乱码。因此cookie中保存中文只能编码,一般使用UTF-8编码即可。session 可以存任意数据类型。
  3. 隐私策略不同:cookie 存储在客户端且是明文显示,比较容易导致信息被窃取。session 存储在服务端,安全性相对 cookie 要好一些。
  4. 存储大小不同:session 存储空间很大,cookie 有限制。

系统想要实现鉴权,可以单独使用 cookie,也可以单独使用 session,但是建议结合两者使用。

token

token 是一种在客户端和服务端之间传递身份信息的方式。当用户登录成功后,服务端会生成一个 token,将其发送给客户端。客户端在后续的请求中,需要将 token 携带在请求头或请求参数中。服务端通过验证 token 的合法性,就可以确定该请求来自哪个用户,并且可以根据用户的权限进行相应的操作。token 可以有效地避免了cookie 的一些安全问题,比如 CSRF 攻击。

token的组成

token是一个由一串字符组成的令牌,用于在计算机系统中进行身份验证和授权。它通常由三个部分组成:标头、有效载荷、签名。

  1. 标头(Header):包含了算法和类型,用于指定如何对有效载荷进行编码和签名。常用的算法有HMAC、RSA、SHA等。
  2. 有效载荷(Payload):包含了一些信息,如用户ID、角色、权限等,用于验证身份和授权。有效载荷可以是加密的,也可以是明文的。
  3. 签名(Signature):是对标头和有效载荷进行签名后得到的值,用于验证token的完整性和真实性。签名通常使用私钥进行签名,并使用公钥进行验证。

一个完整的token包含了标头、有效载荷和签名三个部分,它们一起构成了一个安全的令牌,用于进行身份验证和授权。

token认证流程

  1. 客户端发起登录请求,比如用户输入用户名和密码后登录。
  2. 服务端校验用户名和密码后,将用户 id 和一些其它信息进行加密,生成 token
  3. 服务端将 token 响应给客户端
  4. 客户端收到响应后将 token 存储下来
  5. 下一次发送请求后需要将 token 携带上,比如放在请求头中或者请求体中
  6. 服务端 token 后校验,校验通过则正常返回数据

image.png

session、cookie、token对比

cookie、session、token三者最终的目的都是一样:鉴权和认证

方式特点优点缺点
cookie1.存储在客户端;2.请求自动携带 cookie;3.存储大小 4KB。1.兼容性好;2.容易实现,因为 cookie 会自动携带和存储。1.需要单独解决跨域携带问题,比如多台服务器如何共享 cookie;2.会遭受 CSRF 攻击;3.存储在客户端,不够安全。
session1.存储在服务端;2.存储大小无限制。1.查询速度快,因为是个会话,相当于是在内存中操作;2.结合 cookie 后很容易实现鉴权;3.安全,因为存储在服务端。1.耗费服务器资源,因为每个客户端都会创建 session;2.占据存储空间,session 相当于存储了一个完整的用户信息。
token1.体积很小;2.自由操作存储在哪里1.安全,因为 token 一般只有用户 id,就算被截取了也没什么用;2.无需消耗服务器内存资源,它相当于只存了用户 id,session 相当于存储了用户的所有信息;3.跨域处理较为方便,比如多台服务器之间可以共用一个 token。查询速度慢,因为 token 只存了用户 id,每次需要去查询数据库。(可以使用redis缓存中间件,不必频查数据库,查询一次后缓存使用)