• nodeJS之Cookie和Session(一)


    nodeJS之Cookie和Session(一)

    一:Cookie
       HTTP是一个无状态协议,客户端每次发出请求时候,下一次请求得不到上一次请求的数据,那么如何将上一次请求和下一次请求的数据关联起来呢?
    比如登录官网后,再切换到其他页面时候,那么其他的页面是如何知道该用户已经登录了呢?所以这就可以使用到cookie中的值来判断了。

    cookie它是一个由浏览器和服务器共同协作实现的协议的。那么cookie分为如下几步实现:
    1. 服务器端向客户端发送cookie。
    2. 浏览器将cookie保存。
    3. 之后每次请求都会将cookie发向服务器端。

    1.1 服务器端发送cookie

    服务器发送cookie给客户端是通过HTTP响应报文实现的。在set-Cookie中设置给客户端发送的cookie,cookie格式如下:
    Set-Cookie: name=value; Max-Age=60; Path=/; domain=.domain.com;Expires=Sun, 27 May 2018 05:44:24 GMT; HttpOnly, secure;

    如下图所示

    其中name=value是必选项,其他都是可选的,cookie主要构成如下:

    name: 一个唯一确定cookie的名称。
    value: 存储在cookie中字符串的值。
    domain: cookie对于那个域下是有效的,
    path: 表示这个cookie影响到的路径,浏览器会根据这个配置,向指定的域中匹配的路径发送cookie。
    expires: 失效时间,表示cookie何时失效的时间,如果不设置这个时间,浏览器就会在页面关闭时将删除所有的cookie,不过我们也可以自己设置过期时间。
    注意:如果客户端和服务器端设置的时间不一致,使用expires就会存在偏差。
    max-age: 用来告诉浏览器此cookie多久过期(单位是秒),一般的情况下,max-age的优先级高于expires。
    HttpOnly: 告诉浏览器不允许通过脚本document.cookie去更改值,这个值在document.cookie中也是不可见的,但是在http请求会携带这个cookie,
    注意:这个值虽然在脚本中使不可取的,但是在浏览器安装目录中是以文件形式存在的,这个设置一般在服务器端设置的。
    secure:安全标志,指定后,当secure为true时候,在HTTP中是无效的,在HTTPS中才有效,表示创建的cookie只能在HTTPS连接中被浏览器传递到服务器端进行会话验证,如果是HTTP连接则不会传递该信息,所以一般不会被且听到。

    服务器端设置cookie代码如下:

    const express = require('express');
    const app = express();
    
    app.listen(3001, () => {
      console.log('port listen 3001');
    });
    
    app.get('/', (req, res) => {
      res.setHeader('status', '200 OK');
      res.setHeader('Set-Cookie', 'isVisit=1;domain=/;path=/;max-age=60*1000');
      res.send('欢迎你~');
    });

    直接设置Set-Cookie过于原始,我们可以对cookie的设置过程做如下封装;

    const express = require('express');
    const app = express();
    
    app.listen(3001, () => {
      console.log('port listen 3001');
    });
    
    const serilize = function(name, value, options) {
      if (!name) {
        throw new Errpr('cookie must have name');
      }
      var rets = [];
      value = (value !== null && value !== undefined) ? value.toString() : '';
      options = options || {};
      rets.push(encodeURIComponent(name) + "=" +encodeURIComponent(value));
      if (options.domain) {
        rets.push('domain=' + options.domain);
      }
      if (options.path) {
        rets.push('path=' + options.path);
      }
      if (options.expires) {
        rets.push('expires=' + options.expires.toGMTString());
      }
      if (options.maxAge && typeof options.maxAge === 'number') {
        rets.push('max-age=' + options.maxAge);
      }
      if (options.httpOnly) {
        rets.push('HTTPOnly');
      }
      if (options.secure) {
        rets.push('secure');
      }
      return rets.join(';');
    };
    
    app.get('/', (req, res) => {
      res.setHeader('status', '200 OK');
      res.setHeader('Set-Cookie', serilize('isVisit', '1'));
      res.send('欢迎你~');
    });

    1.2 服务器端解析cookie
    cookie可以设置不同的域和路径,所以对于同一个name value,在不同的域不同的路径下是可以重复的,浏览器会按照与当前请求的url或页面地址最佳匹配的顺序来排定先后顺序。
    服务器端解析代码如下:

    const parse = function(str) {
      if (!str) {
        return;
      }
      const dec = decodeURIComponent;
      var cookies = {};
      const rets = str.split(/s*;s*/g);
      rets.forEach((r) => {
        const pos = r.indexOf('=');
        const name = pos > -1 ? dec(r.substr(0, pos)) : r;
        const val = pos > -1 ? dec(r.substr(pos + 1)) : null;
        // 只需要拿到最匹配的那个
        if (!cookies.hasOwnProperty(name)) {
          cookies[name] = val;
        }
      });
      return cookies;
    };

    因此整个代码如下:

    const express = require('express');
    const app = express();
    
    app.listen(3001, () => {
      console.log('port listen 3001');
    });
    
    const serilize = function(name, value, options) {
      if (!name) {
        throw new Errpr('cookie must have name');
      }
      var rets = [];
      value = (value !== null && value !== undefined) ? value.toString() : '';
      options = options || {};
      rets.push(encodeURIComponent(name) + "=" +encodeURIComponent(value));
      if (options.domain) {
        rets.push('domain=' + options.domain);
      }
      if (options.path) {
        rets.push('path=' + options.path);
      }
      if (options.expires) {
        rets.push('expires=' + options.expires.toGMTString());
      }
      if (options.maxAge && typeof options.maxAge === 'number') {
        rets.push('max-age=' + options.maxAge);
      }
      if (options.httpOnly) {
        rets.push('HTTPOnly');
      }
      if (options.secure) {
        rets.push('secure');
      }
      return rets.join(';');
    };
    const parse = function(str) {
      if (!str) {
        return;
      }
      const dec = decodeURIComponent;
      var cookies = {};
      const rets = str.split(/s*;s*/g);
      rets.forEach((r) => {
        const pos = r.indexOf('=');
        const name = pos > -1 ? dec(r.substr(0, pos)) : r;
        const val = pos > -1 ? dec(r.substr(pos + 1)) : null;
        // 只需要拿到最匹配的那个
        if (!cookies.hasOwnProperty(name)) {
          cookies[name] = val;
        }
      });
      return cookies;
    };
    app.get('/', (req, res) => {
      res.setHeader('status', '200 OK');
      res.setHeader('Set-Cookie', serilize('isVisit', '1'));
      res.send('欢迎你~');
      console.log(parse('isVisit=1'));
    });

    在命令行中运行后可以看到打印出cookie信息键值对,如下:

    1.3 express中的cookie

    express4中操作的cookie使用 cookie-parser模块;如下代码使用:

    const express = require('express');
    const cookieParser = require('cookie-parser');
    const app = express();
    
    app.listen(3000, () => {
      console.log('port listen 3000');
    });
    
    app.use(cookieParser());
    
    app.get('/', (req, res) => {
      if (req.cookies.isVisit) {
        console.log(req.cookies);
        res.send('再次欢迎你');
      } else {
        // cookie设置过期时间为10分钟
        res.cookie('isVisit', 1, {maxAge: 60*1000});
        res.send('欢迎你~');
      }
    });

    二:session

    cookie操作很方便,但是使用cookie安全性不高,cookie中的所有数据在客户端就可以被修改,数据很容易被伪造;所以一些重要的数据就不能放在cookie当中了,并且cookie还有一个缺点就是不能存放太多的数据,为了解决这些问题,session就产生了,session中的数据保留在服务端的。

    2.1 基于Cookie来实现用户和数据的映射
    把数据放到cookie中是不可取的,但是我们可以将口令放在cookie中的,比如cookie中常见的会放入一个sessionId,该sessionId会与服务器端之间会产生
    映射关系,如果sessionId被篡改的话,那么它就不会与服务器端数据之间产生映射,因此安全性就更好,并且session的有效期一般比较短,一般都是设置是
    20分钟,如果在20分钟内客户端与服务端没有产生交互,服务端就会将数据删除。

    session的原理是通过一个sessionid来进行的,sessionid是放在客户端的cookie中,当请求到来时候,服务端会检查cookie中保存的sessionid是否有,
    并且与服务端的session data映射起来,进行数据的保存和修改,也就是说当我们浏览一个网页时候,服务端会随机生成一个1024比特长的字符串,然后存在
    cookie中的sessionid字段中,当我们下次访问时,cookie会带有sessionid这个字段。

    express 中 express-session模块

    在express中操作session可以使用 express-session这个模块,主要方法是session(options),options中包含可选的参数有:
    name: 保存session的字段名称。默认为 connect.sid
    store: session的存储方式,默认存放在内存中。
    secret: 通过设置secret字符串,来计算hash值并放在cookie中,使产生的signedCookie防篡改。
    cookie: 设置存放session id的cookie的相关选项,默认为 (default: { path: '/', httpOnly: true, secure: false, maxAge: null })
    genid: 产生一个新的 session_id 时,所使用的函数, 默认使用 uid2 这个 npm 包。
    rolling: 每个请求都重新设置一个 cookie,默认为 false。
    resave: 即使 session 没有被修改,也保存 session 值,默认为 true

    2.2 在内存中存储session
    如下代码:

    const express = require('express');
    const session = require('express-session');
    const app = express();
    app.listen(3002, () => {
      console.log('port listen 3002');
    });
    app.use(session({
      secret: 'somesecrettoken',
      cookie: { maxAge: 1*60*1000 }  // 1分钟
    }));
    app.get('/', (req, res) => {
      /*
       检查session中的isVisit字段
      */
      if (req.session.isVisit) {
        res.send('再次欢迎你');
      } else {
        req.session.isVisit = true;
        res.send('欢迎你第一次来~');
      }
    });
  • 相关阅读:
    BE Learing 2 名词解释
    mysql学习笔记(二)之一个粗心的问题
    Struts2/XWork < 2.2.0远程执行任意代码漏洞分析及修补
    DataReceivedEventHandler 委托
    JS数组方法汇总 array数组元素的添加和删除
    jQuery学习总结(一)
    js的lock
    mysql学习笔记(一)之mysqlparameter
    Time Span Attack
    Web Vulnerability Scanner 7.0 Patch for 2010_09_21_01
  • 原文地址:https://www.cnblogs.com/tugenhua0707/p/9098132.html
Copyright © 2020-2023  润新知