Cookie 和 Session
Cookie
Cookie 是一段不超过 4KB 的小型文本数据,由一个名称(Name)、一个值(Value)和其它几个用于控制 Cookie 有效期、安全性、使用范围的可选属性组成。它通常是网站为了辨别用户身份,储存在客户端(用户本地)上的数据(通常经过加密),是由用户客户端计算机暂时或永久保存的信息。
问题:express 所有的 render(redirect 等)都会发送一个新的请求,在执行 render 的中间件之前存储数据,可以让整个 render 的中间件里访问到这个数据。但是如果在 render 的中间件里写入数据,则每次执行该数据都会被刷新,无法存储。
- 可以将数据存储为全局变量。但因为该变量在所有请求之间共享,所以也会在用户之间共享(不安全);
- 使用 cookie。cookie 可以帮助我们将数据存储在单个用户的浏览器中,不会影响其他用户。
设置 Cookie
通过setHeader()的Set-Cookie关键字来设置一个 cookie,以=分隔来设置 cookie 的 name 和 value:
res.setHeader("Set-Cookie", "name=value");
cookie 设置完以后,默认情况下浏览器会将它发送到服务器的每个请求。
设置多个 cookie
我们可以同时设置多个 cookie 值:
res.setHeader("Set-Cookie", "name=value; name2=666 ");设置 cookie 失效时长
默认情况下的 cookie 在关闭浏览器后就会失效,我们可以 cookie 设置某个过期时间:
res.setHeader( "Set-Cookie", "name=value; expires=" + new Date(new Date().getTime() + 86409000).toUTCString() );设置 cookie 的 Secure
设置 Secure 意味着cookie 只有在页面通过 HTTPS 请求时才会被设置,保护 cookie 在浏览器和 Web 服务器间的传输过程中不被窃取和篡改。:
res.setHeader('Set-Cookie', 'name=value; Secure');设置 cookie 的 HttpOnly
设置 HttpOnly 意味着我们无法通过客户端的 JS 访问和修改 cookie 的值(前端也可以使用
document.cookie去获取和修改cookie),它保护 cookie 不被跨站脚本攻击窃取或篡改:res.setHeader("Set-Cookie", "name=value; HttpOnly");跨域脚本攻击:客户端 JS 中可能被注入了恶意代码,如果没有限制客户端 JS 访问 cookie,可能会存在身份泄露等问题。
获取 Cookie
通过请求的get('Cookie')方法来获取一个 cookie:
req.get("Cookie"); // 获取的cookie为name=value形式,需要可以自己进行拆分
拆分 cookie:
req.get("Cookie").trim().split("=")[0]; // 获取cookie的name req.get("Cookie").trim().split("=")[1]; // 获取cookie的value
我们可以从浏览器端修改 cookie 的值,所以 cookie 通常用于跨域请求存储数据(cookie 是存储在本地浏览器上,所以它可以发送到我们每个发起请求的页面,所以它可以从一个页面发送到另一个页面,即使这两个页面存在于不同的服务器上),但是一些敏感的数据不能让用户修改,这时候就可以使用 session:
Session
session 是当我们访问服务某个网页的时候,在服务器端的内存开辟一块内存。它存储在服务器的内存中,session 只有对应的浏览器才能访问。
客户端需要告诉服务器它对应的 session,因为 session 也是存储在内存或数据库中的一条条数据。
使用(实现)原理:
我们使用cookie 来存储 session 的 ID,但实际上存储的值并不是 ID,而是经过哈希处理只有服务器可以识别的、以加密方式存储的 ID,这样在 cookie 中就存储了一个我们无法浏览器内部改变、较为安全的值;
当用户保持当前浏览器的情况下再去访问服务器时,会把 session 的 ID 传给服务器,服务器根据 session 的 ID来为用户提供相应的服务。
在 Node(Express)中使用 Session
安装
使用时需下载express-session这个包:
npm install --save express-session
它是 Express 官方拓展包,但并不包含在 Express 里。
初始化 session 中间件
app.js
const express = require("express");
const session = require("express-session"); // 引入express-session
const app = express();
app.use(
session({
// 初始化设置session
secret: "my secret", // 用于标记存储在cookie中的ID的hash
resave: false, // session不会再每完成一个请求就保存 @1
saveUninitialized: false, // 确保不为不需要保存的请求保存session
// cookie: {} // 设置cookie相关配置
})
);
@1:设置
resave: false意味着当 session 发生变化时才会保存,有利于提高性能;
设置 session 的值
使用req.session.xxx = value;来定义存储一个 sessionID 的 cookie,cookie 的 value 为 session 加密的 ID:
exports.postLogin = (req, res, next) => {
// res.setHeader('Set-Cookie', 'loggedIn=true; Secure');
req.session.isLoggedIn = true; // 设置一个session
res.redirect("/");
};
设置的 session 的名字是为了方便我们获取,其在 cookie 中存储的名字统称为
connect.sid。
获取 session 的值
使用req.session.xxx来获取一个 session 的值:
exports.getLogin = (req, res, next) => {
console.log(req.session.isLoggedIn); // 获取session的值
};
存储 session 的值
通过req.session.xxx = value;设置的 session 是存储在内存中的,但内存是有限的,所以我们需要根据不同情况采用不同的存储方式。
对于开发者来说需要存储的用户的 session 较少,内存够用;但生产服务器可能会存储上千万的用户,每个用户都有自己的 session,这很快就会造成内存溢出。
使用 MongoDB 数据库存储
对于 express-session,需要下载
connect-mongodb-session这个包:npm install --save connect-mongodb-session引入 connect-mongodb-session:
app.js
const session = require("express-session"); const MongoDBStore = require("connect-mongodb-session")(session); // 实际上提供了一个需要传递session执行函数 // ... const app = express(); const store = new MongoDBStore({ // 创建数据库存储 uri: "mongodb+srv://<name>:<password>@cluster0.mnle1m2.mongodb.net/shop", // 数据库连接地址 collection: "sessions", // 集合名称(如果不存在就创建) }); app.use( session({ secret: "my secret", resave: false, saveUninitialized: false, store: store, // 设置初始化存储 }) );
删除 session 的值
通过req.session.destroy()来删除一个 session,它内部包含一个错误信息的回调函数:
exports.postLogout = (req, res, next) => {
req.session.destroy((err) => {
console.log(err);
});
};
注意:该删除会将数据库中的 session 删除,浏览器里的 cookie 不会删除,但是其中 cookie 存储的 session 的 ID 在删除后会在数据库中找不到匹配。
确保 session 保存/更新后处理逻辑
有时候我们执行代码还没有等到 session 保存/更新好之后就已经执行完了,如果想等 session 完全保存好之后再进行执行,可以使用req.session.save():
req.session.save((err) => {
console.log(err);
res.redirect("/");
});
小结
Cookie:
存储在浏览器上(客户端);
可以被用户看到和操纵,不应该用来存储一些敏感的信息;
通常不会直接设置 cookie 的值,而是通过类似 session 的包使用 cookie;
可以配置各种各样的 cookie,如什么时候失效(expire);
默认情况下在浏览器关闭时过期,而这类 cookie 被称为
session cookie!session cookie不一定用来识别 session,之所以被称为session cookie是因为它们只在使用当前浏览器页面时存在;
设置 cookie 的失效时间到具体某个日期、年龄,这相当于某种意义上的永久 cookie(它不会在关闭浏览器时就消失);
cookie 能和 session 一起使用,但它并不仅限于和 session 一起使用;
Session:
- 存储在服务器上(服务端);
- 用户无法查看或操纵,适合存储跨域请求保留的敏感数据;
- 可以在 session存储任何形式的数据,通常用来存储用户数据或身份验证状态信息;
- seesion 需要一个 cookie 来识别(这个 cookie 不是
session cookie,而是永久 cookie),一个 cookie 识别一个 session; - 可以选择不同的方式存储 session 数据,如:数据库存储、文件存储等;