因为浏览器无状态的特性,cookie
技术应运而生,cookie
是一个会话级的存储,大小 4KB
左右,用于浏览器将服务器设置的信息从新带给服务器进行验证,不支持跨域,在浏览器清空缓存或超过有效期后失效,不能存放敏感信息,session
是专门用于存储最初设置给浏览器 cookie
数据的地方,咱们本篇就来讨论一下 cookie
和 session
在 NodeJS 中的使用方式。数据库
下面是 cookie
在 Node 原生中的读取和写入方法。跨域
// 原生中操做 cookie const http = require("http"); // 建立服务 http.createServer((req, res) => { if (req.url === "/read") { // 读取 cookie console.log(req.headers.cookie); res.end(req.headers.cookie); } else if (req.url === "/write") { // 设置 cookie res.setHeader("Set-Cookie", [ "name=panda; domain=panda.com; path=/write; httpOnly=true", `age=28; Expires=${new Date(Date.now() + 1000 * 10).toGMTString()}`, `address=${encodeURIComponent("回龙观")}; max-age=10` ]); res.end("Write ok"); } else { res.end("Not Found"); } }).listen(3000);
上面代码建立了一个 http
服务器,能够经过读取 cookie
请求头的值来获取浏览器发来的 cookie
,服务器能够经过给浏览器设置响应头 Set-Cookie
实现对浏览器 cookie
的设置,多个 cookie
参数为数组,在数组内能够规定每一条 cookie
的规则,中间使用一个分号和一个空格隔开。数组
domain
用来设置容许访问 cookie
的域;path
用来设置容许访问 cookie
的路径;httpOnly
用来设置是否容许浏览器中修改 cookie
,若是经过浏览器修改设置过 httpOnly=true
的 cookie
,则会增长一条同名 cookie
,原来的 cookie
不会被修改;Expires
用来设置过时时间,绝对时间,值为一个 GMT
或 UTC
格式的时间;max-age
一样用来设置过时时间,相对时间,值为一个正整数,单位 s
。cookie
默认不支持存储中文,若是存储中文需先使用 encodeURIComponent
方法进行转译,将转译后的结果存入 cookie
,在浏览器获取 cookie
需使用 decodeURIComponent
方法转回中文。浏览器
Koa
是当下流行的 NodeJS 框架,是对原生 Node 的一个轻量的封装,可是内部实现了快捷操做 cookie
的方法,下面是原生中对 cookie
的操做在 Koa
中的写法。缓存
// Koa 中操做 cookie const Koa = require("koa"); const Router = require("koa-router"); // 建立服务和路由 const app = new Koa(); const router = new Router(); // 签名须要设置 key app.keys = ["shen"]; router.get("/read", (ctx, next) => { // 获取 cookie let name = ctx.cookies.get(name) || "No name"; let name = ctx.cookies.get(age) || "No age"; ctx.body = `${name}-${age}`; }); router.get("/write", (ctx, next) => { // 设置 cookie ctx.cookies.set("name", "panda", { domain: "panda.com" }); ctx.cookies.set("age", 28, { maxAge: 10 * 1000, signed: true }); }); // 使用路由 app.use(router.routes()); app.listen(3000);
在 Koa
中将获取和设置 cookie
的方法都挂在了 ctx
上下文对象的 cookies
属性上,分别为 get
和 set
。服务器
cookies.get
的参数为获取 cookie
的键名,返回值为键对应的值,cookies.set
的第一个参数一样为 cookie
的键名,第二个参数为键对应的值,第三个参数为一个对象,用来配置该条 cookie
的规则,如 domain
、path
和过时时间等,这里 maxAge
值为毫秒数。cookie
注意:Koa
中设置的 cookie
默认不容许浏览器端经过 document.cookie
获取,可是服务器也能够被欺骗,好比使用 postman
发送一个带 Cookie
请求头的请求,服务器能够经过设置签名来预防,即添加 signed
选项并将值设置为 true
。session
cookies
对象都是挂在 ctx
上来实现的,使用过 Koa
都知道若是要操做 ctx
就会用到中间件的思想,咱们这就看看这两个方法使用原生封装的过程。app
// Koa 中 ctx.cookies 对象 get 和 set 方法的原理 const Koa = require("koa"); const querystring = require("querystring"); const app = new Koa(); app.use(async (ctx, next) => { // 获取 cookie const get = key => { let cookies = ctx.get("cookie") || ""; return querystring.parse(result, "; ")[key]; }; // 设置 cookie,存储全部的 cookie,等于 setHeader 中的第二个参数 let cookies = []; const set = (key, val, options = {}) => { // 用于构造单条 cookie 和权限等设置的数组,默认存放这条 cookie 的键和值 let single = [`${key}=${encodeURIComponent(val)}`]; // 下面是配置 if (options.domain) { arr.push(`domain=${options.domain}`); } if (options.maxAge) { arr.push(`Max-Age=${options.maxAge}`); } if (options.path) { arr.push(`path=${options.path}`); } if (options.httpOnly) { arr.push(`HttpOnly=true`); } // 将配置组合到 single 中后转为字符串存入 cookies cookies.push(single.join("; ")); // 设置给浏览器 ctx.set("Set-Cookie", cookies); } // 将获取和设置 cookie 的方法挂在 cookies 对象上 ctx.cookies = { get, set }; await next(); });
在 get
方法内部获取 cookie
请求头的值并根据传入的 key
获取值,set
方法内,将传入的键值和选项拼接成符合 cookie
的字符串,经过 Set-Cookie
响应头设置给浏览器。框架
正常 session
是存放在数据库中的,咱们这里为了方便就用一个名为 session
的对象来代替。
// 原生中使用 session const http = require("http"); const uuid = require('uuid/v1'); // 生成随字符串 const querystring = require("querystring"); // 存放 session const session = {}; // 建立服务 http.createServer((req, res) => { if (req.url === "/user") { // 取出 cookie 存储的用户 ID let userId = querystring.parse(req.headers["cookie"], "; ")["study"]; if (userId) { if (session[userId].studyCount === 0) res.end("您的学习次数已用完"); session[userId].studyCount--; } else { // 生成 userId userId = uuid(); // 将用户信息存入 session session[userId] = { studyCount: 30 }; // 设置 cookie req.setHeader("Set-Cookie", [`study=${userId}`]); } // 响应信息 res.end(` 您的用户 ID 为 ${userId}, 剩余学习次数为:${session[userId].studyCount} `); } else { res.end("Not Found"); } }).listen(3000);
上面写的案例是一个网校的场景,一个新用户默认有 30
次学习机会,之后每次访问服务器学习次数减 1
,若是 studyCount
值为 0
,则提示学习次数用完,不然提示当前用户的 ID
和剩余学习次数,session
中存储的是每个用户 ID
对应的剩余学习次数,这样就不会轻易的被修改学习剩余次数,由于服务器只认用户 ID
,再经过 ID
去更改对应的剩余次数(固然忽略了别人冒充这个 ID
的状况,只能减,不能加),这样就不会由于篡改 cookie
而篡改用户存在 session
中的数据,除非连整个数据库都拖走。
咱们接下来使用 Koa
实现和上面一摸同样的场景,在 Koa
的社区中提供了专门操做 session
的中间件 koa-session
,使用前需安装。
// Koa 中使用 session const Koa = require("koa"); const Router = require("koa-router"); const session = requier("koa-session"); const uuid = require("uuid/v1"); // 建立服务和路由 const app = new Koa(); const router = new Router(); // cookie 的签名 app.keys = ["panda"]; // 使用 koa-session 中间件 app.use(session({ key: "shen", maxAge: 10 * 1000 }, app)); router.get("/user", (ctx, next) => { // 取出 cookie 存储的用户 ID let userId = ctx.cookie("study"); if (ctx.session.userId) { if (ctx.session[userId].studyCount === 0) res.end("您的学习次数已用完"); ctx.session[userId].studyCount--; } else { // 生成 userId userId = uuid(); // 将用户信息存入 session ctx.session[userId] = { studyCount: 30 }; // 设置 cookie ctx.cookies.set("study", userId); } // 响应信息 ctx.body = ` 您的用户 ID 为 ${userId}, 剩余学习次数为:${session[userId].studyCount} `; }); // 使用路由 app.use(router.routes()); app.listen(3000);
使用 Koa
的 koa-session
之后,再也不须要咱们建立 session
对象进行存储,而且 cookie-session
中间件帮咱们封装了 API 能够直接操做 mongo
和 MySQL
数据库,上面代码中与用原生相比还增长了 cookie
和 session
的签名和过时时间,比原生写起来要方便不少。
本篇内容更偏向于 cookie
和 session
在 NodeJS 中的使用,没有过多的叙述理论性的内容,cookie
和 session
是相互依存的,也就是说共同使用的,如今已经有 JWT 的方案来替代,由于相比较下有不少优势,但某些项目和特殊场景还在使用 cookie
和 session
,因此仍是写了这一篇,若是对 JWT 感兴趣能够看 经过一个案例理解 JWT。