cookie
cookie 是一种在客户端存储数据的技术,它是由服务器发送给客户端的小型文本文件,用来记录用户的一些信息,存储在客户端的浏览器中,大小限制大致在 4KB 左右。
cookie 主要用于以下三个方面:
- 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
- 个性化设置(如用户自定义设置、主题等)
- 浏览器行为跟踪(如跟踪分析用户行为等)
cookie 主要特点:
- cookie 存储在客户端
- cookie 不可跨域,但是在如果设置了 domain,那么它们是可以在一级域名和二级域名之间共享的
cookie虽然可以用来存储各种数据,但随着更多浏览器存储方案的出现,cookie存储数据这种方式逐渐被取代,主要原因有如下:
- cookie 有存储大小限制,4KB 左右
- 浏览器每次请求会携带cookie在请求头中
- 字符编码为 Unicode,不支持直接存储中文
- 数据可以被轻易查看
cookie的主要属性
属性名 | 描述 |
---|---|
name | cookie 的名称 |
value | cookie 的值 |
comment | cookie 的描述信息 |
domain | 可以访问该 cookie 的域名 |
expires | cookie 的过期时间,具体某一时间 |
maxAge | cookie 的过期时间 |
path | cookie 的使用路径 |
secure | cookie 是否使用安全协议传输,比如 SSL 等 |
version | cookie 使用的版本号 |
isHttpOnly | 指定该 cookie 无法通过 JavaScript 脚本拿到,比如 Document.cookie 属性、XMLHttpRequest 对象和 Request API 都拿不到该属性。这样就防止了该 cookie 被脚本读到,只有浏览器发出 HTTP 请求时,才会带上该 cookie |
流程
客户端向服务端发送请求时,服务端需要记录该用户的状态和一些信息,服务端就会设置响应头的Set-Cookie
字段信息来通知客户端保存cookie。当客户端再向服务端发起请求时,客户端会自动在请求头中将相应的cookie信息发送给服务端,服务端接通过读取客户端发送过来的cookie信息,就可以判断是来自哪个客户端发送过来的请求,以此来辨认用户状态。
第一次客户端发起请求服务端返回的响应信息如下:响应头Response Headers
的Set-Cookie
的字段信息来通知客户端保存Cookie。
当客户端再次发送请求到服务端时,就携带了cookie信息在请求头Request Headers
中一同发送给了服务端:
通过 cookie 来实现用户确定或者权限确定流程图如下:
- 客户端发送请求到服务端(比如登录请求)
- 服务端收到请求后生成一个 session 会话
- 服务端响应客户端,并在响应头中设置
Set-Cookie
字段。Set-Cookie
字段里面包含了 sessionId,它的格式如下:Set-Cookie: value; expires=date;domain=domain;path=path;secure
。其中 sessionId就是用来标识客户端的 - 客户端收到该请求后,如果服务端响应头设置了
Set-Cookie
字段,那么客户端发送其它请求就会在请求头中自动携带 cookie,cookie 中携带有用户信息等 - 服务端接收到请求,验证 cookie 信息,比如通过 sessionId 来判断是否存在会话,存在则正常响应
session
session在网络应用中称为会话控制,是服务器为了保存用户状态而创建的一个特殊的对象。session就是一个存储于服务器端的对象,用于存储信息。
服务器会为每一个浏览器(客户端)创建一个唯一的session,这个session是服务器端共享,每个浏览器(客户端)独享。可以在session对象中存储数据,实现数据共享。
session的存储形式
session类似于一个Map字典,里面可以存放多个键值对,是以key-value键值对的形式进行存放的。key必须是一个字符串,value是一个对象。
session底层实现机制
session是每一个浏览器(客户端)所唯一的,其实现机制是:HTTP请求的请求头中可以携带cookie,这个cookie包含了sessionId,这个sessionId表示的就是session所对应的sessionId,这个是由服务端创建的,并且是唯一的。服务端在使用session时,会根据sessionId来进行不同操作。
- 客户端(浏览器)发送首次请求到服务端
- 服务端收到请求,发现客户端没有提供会话ID(缺少一个值为
sessionId
的cookie) - 服务端创建一个新的会话,生成一个唯一的会话ID,并将创建的
sessionId
存储在cookie 中返回给客户端,以及将会话数据保存在服务端存储的session对象中(可以将session对象存储在内存、数据库等) - 服务端将会话ID放在响应头的
Set-Cookie
字段中发送回客户端,客户端浏览器会存储这个会话ID。 - 自此,客户端的每个请求都会附带这个会话ID的
sessionId
cookie。 - 服务器根据收到的会话ID,从服务端的存储中查找相应的会话数据,以恢复用户的会话状态
通常情况下,cookie 和 session 都是结合着来用,服务器端将 session 的唯一标识 sessionId 通过设置响应头Set-Cookie
的方式响应给客户端,客户端将 sessionId 存到 cookie 中。当然可以单独只使用 cookie 或者单独只使用 session,这里将 cookie 和 session 结合着来用,如下图:
使用express框架管理session(会话)
需要使用express-session
中间件,可以使用下面的命令安装这个模块:
ssh
npm i express-session
举例:使用session记录页面访问次数
js
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端口已启动");
});
当客户端第一次请求时,服务端给req.session写入一个views属性并在响应头返回保存了该属性的session对象的cookie给客户端。当客户端再次发起请求时会在请求头携带保存了sessionId的cookie给服务端,服务端把req.session.views加1,然后返回给客户端。客户端再次请求之后会响应用户访问的次数。
session与cookie的区别
cookie 和 session,它们两者之间主要是通过 sessionId 关联起来的,所以sessionId 是 cookie 和 session 之间的桥梁。
- 作用范围不同:cookie 保存在客户端(浏览器),session 保存在服务端。
- 存取方式不同:cookie 保存中文属于Unicode字符,在内存中占4个字节,而英文属于ASCII字符,内存中只占2个字节。cookie中使用Unicode字符时需要对Unicode字符进行编码,否则会乱码。因此cookie中保存中文只能编码,一般使用UTF-8编码即可。session 可以存任意数据类型。
- 隐私策略不同:cookie 存储在客户端且是明文显示,比较容易导致信息被窃取。session 存储在服务端,安全性相对 cookie 要好一些。
- 存储大小不同:session 存储空间很大,cookie 有限制。
系统想要实现鉴权,可以单独使用 cookie,也可以单独使用 session,但是建议结合两者使用。
token
token 是一种在客户端和服务端之间传递身份信息的方式。当用户登录成功后,服务端会生成一个 token,将其发送给客户端。客户端在后续的请求中,需要将 token 携带在请求头或请求参数中。服务端通过验证 token 的合法性,就可以确定该请求来自哪个用户,并且可以根据用户的权限进行相应的操作。token 可以有效地避免了cookie 的一些安全问题,比如 CSRF 攻击。
token的组成
token是一个由一串字符组成的令牌,用于在计算机系统中进行身份验证和授权。它通常由三个部分组成:标头、有效载荷、签名。
- 标头(Header):包含了算法和类型,用于指定如何对有效载荷进行编码和签名。常用的算法有HMAC、RSA、SHA等。
- 有效载荷(Payload):包含了一些信息,如用户ID、角色、权限等,用于验证身份和授权。有效载荷可以是加密的,也可以是明文的。
- 签名(Signature):是对标头和有效载荷进行签名后得到的值,用于验证token的完整性和真实性。签名通常使用私钥进行签名,并使用公钥进行验证。
一个完整的token包含了标头、有效载荷和签名三个部分,它们一起构成了一个安全的令牌,用于进行身份验证和授权。
token认证流程
- 客户端发起登录请求,比如用户输入用户名和密码后登录。
- 服务端校验用户名和密码后,将用户 id 和一些其它信息进行加密,生成 token
- 服务端将 token 响应给客户端
- 客户端收到响应后将 token 存储下来
- 下一次发送请求后需要将 token 携带上,比如放在请求头中或者请求体中
- 服务端 token 后校验,校验通过则正常返回数据
session、cookie、token对比
cookie、session、token三者最终的目的都是一样:鉴权和认证
方式 | 特点 | 优点 | 缺点 |
---|---|---|---|
cookie | 1.存储在客户端;2.请求自动携带 cookie;3.存储大小 4KB。 | 1.兼容性好;2.容易实现,因为 cookie 会自动携带和存储。 | 1.需要单独解决跨域携带问题,比如多台服务器如何共享 cookie;2.会遭受 CSRF 攻击;3.存储在客户端,不够安全。 |
session | 1.存储在服务端;2.存储大小无限制。 | 1.查询速度快,因为是个会话,相当于是在内存中操作;2.结合 cookie 后很容易实现鉴权;3.安全,因为存储在服务端。 | 1.耗费服务器资源,因为每个客户端都会创建 session;2.占据存储空间,session 相当于存储了一个完整的用户信息。 |
token | 1.体积很小;2.自由操作存储在哪里 | 1.安全,因为 token 一般只有用户 id,就算被截取了也没什么用;2.无需消耗服务器内存资源,它相当于只存了用户 id,session 相当于存储了用户的所有信息;3.跨域处理较为方便,比如多台服务器之间可以共用一个 token。 | 查询速度慢,因为 token 只存了用户 id,每次需要去查询数据库。(可以使用redis缓存中间件,不必频查数据库,查询一次后缓存使用) |