【Node】cookie、sessionStorage、localStorage 与 身份认证

cookie

  1. 以 key-value 的形式存储在浏览器

  2. 可配置一些用于控制 cookie 有效期、安全性、适用范围… 的可选属性
    默认情况下,页面关闭 → 清除数据;也可以手动删除

  3. key-value 的 key 和 value 都只能是 [字符串]

    对于 [数字] value,存储时会自动将其转换为字符串

    对于 [对象]、[数组] value,我们可以使用 JSON.stringify() 进行转换

  4. 客户端发送请求时,会自动把当前域名下所有未过期的 cookie 一同发送到服务器

  5. 因为客户端与服务器交互时 会带上 cookie,所以 cookie 相对不安全
    不建议服务器将重要的隐私数据,以 cookie 的形式发送给浏览器

浏览器操作 cookie

  • 在浏览器中 (客户端),可通过 document.cookie 操作 cookie
document.cookie = "name=superman"; // 存数据
console.log(document.cookie); // 获取所有 cookie
  • 可以在操作 cookie 的时候配置 cookie 的字段
    domain:cookie 生效的域名
    path:cookie 生效的路由路径
    expires / max-age:cookie 的有效期;默认为 session,即页面关闭就清空数据
    expires 为指定时间,从 1970-1-1-0-0-0 开始算;max-age 为相对时间
document.cookie = "age=18; max-age=30"; // 30s 后失效

node.js 操作 cookie

  • 在 node.js 中 (服务器),可通过插件 cookie-parser 操作 cookie
  1. 下载第三方中间件 cookie-parser - npm i cookie-parser
  2. 引入、注册中间件 cookie-parser
const cookieParser = require('cookie-parser');
app.use(cookieParser());
  • res.cookie(key, value [, options]):用于设置 cookie

可通过配置对象 options 设置 cookie 的字段:

  1. domain:String;cookie 生效的域名。默认为应用程序的域名
  2. path:String;cookie 生效的路由路径。默认为 /
  3. expires:Date;cookie (格林尼治时间)的失效日期。如果未指定或设置为 0,则创建会话 cookie
  4. maxAge:Number;相对于当前时间设置失效时间,以毫秒为单位
  5. secure:Boolean;将 cookie 标记为仅用于 HTTPS
  6. encode:Function;为 cookie 值编码的同步函数。默认为 encodeURLComponent
  7. httpOnly:Boolean;将 cookie 标记为仅可由 web 服务器访问,前端不可访问;默认为 false
  8. signed:Boolean;指示是否应该对 cookie 进行签名
const express = require("express");
const app = express();
app.listen(8080, () => console.log("http://127.0.0.1:8080"));

app.use(express.static(__dirname + "/public"));

// 引入、注册 cookie-parser
const cookieParser = require('cookie-parser');
app.use(cookieParser());

app.get("/setCookie", (req, res) => {
    // 设置 cookie
    res.cookie('name', 'superman', {
        maxAge: 10000
    });
    res.send("setCookie");
});

app.get("/getCookie", (req, res) => {
    // 获取 cookie
    console.log("cookies", req.cookies); // cookies { name: 'superman' }
    res.send("getCookie");
});
<body>
    <button id="but1">设置cookiebutton>
    <button id="but2">获取cookiebutton>
body>

<script src="https://unpkg.com/axios/dist/axios.min.js">script>
<script>
    // 设置 cookie
    but1.onclick = async () => {
        let res = await axios({
            url: "/setCookie"
        });
        console.log("data", res.data); // data setCookie
    }
    // 获取 cookie
    but2.onclick = async () => {
        let res = await axios({
            url: "/getCookie"
        });
        console.log("data", res.data); // data getCookie
    }
script>

cookie 的工作流程

客户端第 1 次请求服务器时,服务器向客户端返回一个用于身份认证的 cookie,客户端会将该 cookie 保存在浏览器中

随后,当客户端请求服务器时,浏览器会自动将当前域名下所有未过期的 cookie (当然也就包括身份认证相关的 cookie) 以 [请求头] 的形式发送给服务器,服务器即可验证客户端身份

sessionStorage

  • 以 key-value 的形式存储在服务器

  • 默认情况下,页面关闭 → 清除数据;也可以手动删除

  • key-value 的 key 和 value 都只能是 [字符串]

    对于 [数字] value,存储时会自动将其转换为字符串

    对于 [对象]、[数组] value,我们可以使用 JSON.stringify() 进行转换

浏览器操作 session

  • 在浏览器中 (客户端),可通过 sessionStorage 操作 sessionStorage
console.log(sessionStorage); // 打印 sessionStorage 对象
sessionStorage.setItem("name", "superman"); // 存数据
const name = sessionStorage.getItem("name"); // 取数据
sessionStorage.removeItem("name"); // 删除数据
sessionStorage.clear(); // 清空数据
  • 如果读取的 key 不存在,读取到的 value 会为 null
  • JSON.parse(null) 返回 null

node.js 操作 session

  • 在 node.js 中 (服务器),可通过插件 express-session 操作 sessionStorage
  1. 下载第三方中间件 express-session - npm i express-session
  2. 引入、注册中间件 express-session:
const session = require("express-session");
app.use(session({
    secret: "string key", // 加密的字符串,里面内容可以先随便写
    resave: false, // 强制保存 session,即使它没变化
    saveUninitialized: true, // 强制将未初始化的 session 存储
}));
  1. 配置好 express-session 后,即可通过 req.session 获取、配置 session 啦
console.log(req.session); // 获取 session

req.session.key = value; // 添加 session 属性
console.log(req.session.key); // 获取 session 属性
req.session.key = null; // 删除 session 属性

req.session.destory(err => {}); // 清空 session - 一般在退出登陆时执行

配合 cookie 操作 session

app.use(session({
    secret: "string key",
    resave: false,
    saveUninitialized: true,
    cookie: { // 配置 cookie
        maxAge: 1000 * 60 * 60 * 24 * 3, // 三天
        secure: false // 是否仅 https 可用
    }
}));

session & MongoDB

  1. npm i express-session connect-mongo
  2. 引入需要的模块:
const session = require("express-session");
const MongoStore = require("connect-mongo")
  1. 配置中间件:
app.use(session({
    secret: 'keyboard cat',
    resave: false,
    saveUninitialized: true,
    store: MongoStore.create({  mongoUrl: 'mongodb://localhost:27017/node' }) // 存入 MongoDB
}));

session 的工作流程

浏览器第一次向服务器发送请求时,服务器会建立一个 session 对象,用于存储用户信息
随后 服务器将 session-key 返回给客户端,客户端将 session-key 存储为 cookie
浏览器再次访问时,会带上 cookie (包含 session-key),通过 session-key 找到对应的 session 对象
如此,便可根据 session-key 获取对应的数据、执行对应的操作

localStorage

  • 以 key-value 的形式存储在浏览器中

  • 只要不删除,数据就会永久存储

  • key-value 的 key 和 value 都只能是 [字符串]

    对于 [数字] value,存储时会自动将其转换为字符串

    对于 [对象]、[数组] value,我们可以使用 JSON.stringify() 进行转换

浏览器操作 localStorage

  • 在浏览器中 (客户端),可通过 localStorage 操作 localStorage
console.log(localStorage); // 打印 localStorage 对象
localStorage.setItem("name", "superman"); // 存数据
const name = localStorage.getItem("name"); // 取数据
localStorage.removeItem("key"); // 删除数据
localStorage.clear(); // 清空数据
  • 如果读取的 key 不存在,读取到的 value 会为 null
  • JSON.parse(null) 返回 null

三者的对比

基本概念

  1. cookie:主要用来保存登录信息;前端给后端发请求时会带上 cookie,后端会根据 cookie 做出对应的操作
  2. sessionStorage:用于在当前页面中保存一些数据;页面关闭后数据会被清空
  3. localStorage:localStorage 类似 sessionStorage,但存储在 localStorage 中的数据默认是长期保存的

存储大小

  • 不同浏览器可能不同
  1. cookie:一般不超过 4k,浏览器限制一个站点最多保存 20 个 cookie
  2. sessionStorage:大约 5M
  3. localStorage:大约 5M

数据有效期

  1. cookie:cookie 的有效期一般由服务器生成。默认情况下,关闭页面 则失效
    可以设置失效时间,如果设置了失效时间,cookie 就会存储在硬盘中,过期 则失效
  2. sessionStorage:关闭页面 则失效
  3. localStorage:永久有效,除非手动删除

作用域

  1. cookie:在同源窗口中共享
  2. sessionStorage:在当前页面中共享
  3. localStorage:在同源窗口中共享
  • 同源:协议、域名、端口号都一样;不一样则为跨域

通信 & 存储位置

  1. cookie:存储在客户端(浏览器)中,能在浏览器和服务器之间来回传递
    所以,如果 cookie 保存过多数据会影响性能
  2. sessionStorage:保存在服务器中,不参与通信
  3. localStorage:保存在客户端(浏览器)中,不参与通信

身份认证

  1. 服务端渲染 - Session 认证机制
  2. 前后端分离 - JWT 认证机制

Session 认证机制

HTTP 协议的无状态性

客户端的 HTTP 请求都是独立的,请求之间没有直接的关系,服务器不会主动保留每次 HTTP 请求的状态。
所以需要在每次请求时 带上用户数据对应的 id,服务器拿到 id 并获取对应的数据、执行对应的操作。
而让所有请求都带上指定数据的技术便是 cookie

cookie & session

客户端第 1 次请求服务器时,服务器会建立一个 session 对象,用于存储用户信息
随后 服务器向客户端返回一个身份认证的 cookie —— session-key,客户端会将该 cookie 保存在浏览器中

当客户端再次请求服务器时,浏览器会自动将当前域名下所有未过期的 cookie (当然也就包括 session-key) 以 [请求头] 的形式发送给服务器,服务器通过 session-key 找到对应的 session 对象,即可验证客户端身份、获取对应的数据、执行对应的操作

可知,用户数据都是存储在服务端的,浏览器存储 cookie 数据只是用户数据对应的 id → session-key

局限性

  1. session 认证机制需要配合 cookie 才能实现
    由于 cookie 默认不支持跨域访问,所以,当涉及前端跨域请求后端接口时,需要做额外的配置,才能实现跨域 session 认证

  2. 如果在特定时间有大量用户访问服务器的话,服务器可能就要存储大量 session-key

  3. 如果使用多台服务器:服务器之间存储的 session-key 需要共享

  4. 如果使用数据库存储 session-key:要是数据库奔溃,会影响服务器获取 session-key

JWT 认证机制

JWT - JSON Web Token,是目前最流行的跨域认证解决方案

  • token 字符串:由用户信息加密形成的字符串,存储在客户端
  • 用户信息以 [token 字符串] 的形式存储在客户端中。客户端每次请求都会带上 token 字符串到服务器,服务器通过将 [token 字符串] 还原成用户信息 来认证用户的身份
  • 此时服务器只需存储 JWT 签名密文

使用

  • 客户端收到服务器返回的 token 后,通常会将其存储在 localStorage / sessionStorage 中
  • 此后,客户端每次与服务器通信,都要带上这个 token
  • 推荐把 token 放在 HTTP 请求头Authorization 字段中
Authorization: Bearer <token>

JWT 的组成部分

  • JWT 通常由三部分组成,分别是 Header - 头部、Payload - 有效荷载、Signature - 签名
  • 三者之间使用 . 隔开
Header.Payload.Signture
  • Header:一些配置信息,eg:有效期、用什么算法来生成 Signture…
  • Payload:用户信息对象
  • Signture:Header & Payload 经过 base64 编码生成两段字符串,两段字符串基于 [加密算法] & [JWT 签名密文] 得到 Signture

node.js 实现 JWT

  1. npm i jsonwebtoken express-jwt
    jsonwebtoken - 用于将 [key-value 形式的用户信息]转成 [token 字符串]
    express-jwt - 用于将 [token 字符串] 解析还原成 [JSON 对象]
  2. 导入 jsonwebtoken、express-jwt
const jwt = require("jsonwebtoken");
const expressJWT = require("express-jwt");
  1. 定义 secret 密钥:为了保证 token 的安全性,我们需要定义一个用于 [加密]、[解密] 的 secret 密钥
    ① 生成 token 时,需要使用 secret 密钥对用户信息进行加密,最终得到加密好的 token 字符串
    ② 把 token 字符串解析还原成 JSON 对象时,需要使用 secret 密钥进行解密
const secretKey = "superman token"; // 可以是任意字符串,越复杂安全性越好
  1. 登陆成功后,生成 token:
    调用 jsonwebtoken 依赖包提供的 sign() 方法,将用户的信息加密成 token 字符串,响应给客户端
app.post("/api/login", (req, res) => {
    // ...
    res.send({
        status: 200,
        message: "login success!",
        // 调用 sing() 方法,生成 token 字符串
        token: jwt.sign(
            {username: "superman"}, // Payload - 用户信息对象
            secretKey, // 加密密钥
            {algorithm: "HS256", expiresIn: "30s"} // Header - 配置加解密算法、有效期 (s-秒、h-小时)
        );
    });
});
  1. 将 token 字符串还原为 JSON 对象
    客户端每次访问那些有权限的接口时,都需要配置 [请求头中的 Authorization 字段],将 token 字符串发送到服务器进行身份认证
    此时,服务器可以通过 express-jwt 这个中间件,自动将客户端发送过来的 token 字符串解析还原成 JSON 对象
    被还原的用户信息对象,会被挂载到 req.user / req.auth
// 使用 app.use() 注册中间件
app.use( expressJWT({ secret: secretKey }).unless({ path: [/^\/api\//] }) );
// expressJWT({ secret: secretKey }) - 解析 token 的中间件
// .unless({ path: [/^\/api\//] }) - 指定不需要访问权限的接口
  1. 配置完 express-jwt 后,即可通过 req.user / req.auth 获取解析后的用户信息
    注意:千万不要把密码等敏感信息加密到 token 中!
app.get("/admin/getinfo", (req, res) => {
    console.log(req.user);
    res.send({
        status: 200,
        message: "获取用户信息成功!",
        data: req.user // 将用户信息发送给客户端
    });
});
  1. 捕获解析 token 失败后产生的错误:
    当使用 express-jwt 解析 token 字符串时,如果客户端发送过来的 token 字符串 [过期] / [不合法],会产生一个解析失败的错误
    可以通过 express 的错误中间件,捕获、处理这个错误
app.use((err, req, res, next) => {
    // token 解析失败导致的错误
	if(err.name === "UnauthorizedError")
        return res.send({ status: 401, message: "无效的 token" });
    // 其他原因导致的错误
    res.send({ status:500, message: "未知的错误" });
});

你可能感兴趣的:(Node,node.js,前端,javascript)