NodeJS 使用 cookie 和 session

在这里插入图片描述


阅读原文


前言

因为浏览器无状态的特性,cookie 技术应运而生,cookie 是一个会话级的存储,大小 4KB 左右,用于浏览器将服务器设置的信息从新带给服务器进行验证,不支持跨域,在浏览器清空缓存或超过有效期后失效,不能存放敏感信息,session 是专门用于存储最初设置给浏览器 cookie 数据的地方,咱们本篇就来讨论一下 cookiesession 在 NodeJS 中的使用方式。数据库


cookie 的基本使用

一、NodeJS 原生操做 cookie

下面是 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=truecookie,则会增长一条同名 cookie,原来的 cookie 不会被修改;
  • Expires 用来设置过时时间,绝对时间,值为一个 GMTUTC 格式的时间;
  • max-age 一样用来设置过时时间,相对时间,值为一个正整数,单位 s

cookie 默认不支持存储中文,若是存储中文需先使用 encodeURIComponent 方法进行转译,将转译后的结果存入 cookie,在浏览器获取 cookie 需使用 decodeURIComponent 方法转回中文。浏览器

二、Koa 中操做 cookie

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 属性上,分别为 getset服务器

cookies.get 的参数为获取 cookie 的键名,返回值为键对应的值,cookies.set 的第一个参数一样为 cookie 的键名,第二个参数为键对应的值,第三个参数为一个对象,用来配置该条 cookie 的规则,如 domainpath 和过时时间等,这里 maxAge 值为毫秒数。cookie

注意:Koa 中设置的 cookie 默认不容许浏览器端经过 document.cookie 获取,可是服务器也能够被欺骗,好比使用 postman 发送一个带 Cookie 请求头的请求,服务器能够经过设置签名来预防,即添加 signed 选项并将值设置为 truesession

三、Koa 操做 cookie 方法的原理

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 的基本使用

一、NodeJS 原生使用 session

正常 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 中使用 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);

使用 Koakoa-session 之后,再也不须要咱们建立 session 对象进行存储,而且 cookie-session 中间件帮咱们封装了 API 能够直接操做 mongoMySQL 数据库,上面代码中与用原生相比还增长了 cookiesession 的签名和过时时间,比原生写起来要方便不少。


总结

本篇内容更偏向于 cookiesession 在 NodeJS 中的使用,没有过多的叙述理论性的内容,cookiesession 是相互依存的,也就是说共同使用的,如今已经有 JWT 的方案来替代,由于相比较下有不少优势,但某些项目和特殊场景还在使用 cookiesession,因此仍是写了这一篇,若是对 JWT 感兴趣能够看 经过一个案例理解 JWT

相关文章
相关标签/搜索