微信小程序模拟cookie的实现


Posted in Javascript onJune 20, 2018

开发背景

现有系统已经有一套完整的接口,用户状态、验证都是基于 cookie 的。

部分业务要上小程序版本,众所周知,微信小程序不支持 cookie 的。要上线的业务,最好的方式还是基于现有这套接口做,改动不大,也最快。

模拟 cookie

通过浏览器的开发工具,Network 栏查看请求,浏览器中的 cookie 会携带在每个 http 的 Request Headers 里面,用 Cookie 作为键名。

那么,在微信官方请求方式 wx.request 中,我们设置 header,添加一个 Cookie 应该可以得以模拟。

问题又来了,怎么获取到服务器返回的 cookie 呢。

通过登录接口(登录的时候,服务器端会植入 cookie 作为 session),查看 http 返回头。

wx.request({
  url: '/api/login',
  success: (data) => {
    if(data.statusCode === 200) {
      console.log(data);
      // data 中应该会有 Set-Cookie 或 set-cookie 的字样,嗯,那就是服务器种下的 cookie
    }
  }
})

拿到 cookie 存入本地中,下次请求数据的时候直接塞进去,完美。

格式化 cookie

原本以为 cookie 只需要一进一出就可以完美模拟,实际操作才发现,携带上去的 cookie 服务器无法识别。

服务器返回的 cookie 中,会携带上很多储存用的字段,例如 path=/;

// 服务器放回的 cookie
let cookie = 'userKey=1234567890; Path=/; Expires=Thu, 21 Jun 2018 13:15:08 GMT; HttpOnly,userId=111; Path=/; Expires=Thu, 21 Jun 2018 13:15:08 GMT,nickName=; Path=/; Expires=Thu, 21 Jun 2018 13:15:08 GMT,userName=111111; Path=/; Expires=Thu, 21 Jun 2018 13:15:08 GMT,imgUrl=; Path=/; Expires=Thu, 21 Jun 2018 13:15:08 GMT';

// 模拟的是需要的格式样式
let virtualCookie = 'userKey=1234567890; userName=111111; userId=111;';

妈耶~要怎么过滤呢。

简单粗糙的写了一个过滤方案。

// cookie 的本地存储位置
const COOKIE_KEY = '__cookie_key__';

/**
 * 格式化用户需要的 cookie
 */
const normalizeUserCookie = (cookies = '') => {
  let __cookies = [];
  (cookies.match(/([\w\-.]*)=([^\s=]+);/g) || []).forEach((str) => {
    if (str !== 'Path=/;' && str.indexOf('csrfToken=') !== 0) {
      __cookies.push(str);
    }
  });
  wx.setStorageSync(COOKIE_KEY, __cookies.join(' '));
};

csrfToken 是接下来配合 Egg.js 用的,Path=/; 在某些应用下会是 path=/;

normalizeUserCookie 主要是过滤了 xx=xxx; 这样的数据,然后排除 path=/; 这样无意义的数据。

在登录接口的时候,存上 cookie,在接下来的请求中带上,那么,应该、没错、可能、可以模拟了。

配合 Egg.js

Egg 内置的 egg-security 插件默认对所有『非安全』的方法,例如 POST,PUT,DELETE 都进行 CSRF 校验。
Egg.js 虽然可以在配置中关闭 CSRF,但是,如果一定要使用呢?

首先,要弄明白一件事,csrfToken 怎么来的。

经过多次验证得知,当 http 请求时,在约定位置没有携带上 csrfToken 值,此次请求会在返回的 cookie 中携带上一个新的 csrfToken;当本次请求已携带上值,就不会产生成 csrfToken。当约定位置带上的 csrfToken 与 cookie 里面的 csrfToken 一致时,通过验证。

接上面的 格式化用户需要的 cookie 操作,先抛开 csrfToken 单独处理用户状态等。

在每次请求结束后,试着单独拿 cookie 中可能存在的 csrfToken,有值就缓存,没值跳过用旧值。

封装一个 Ajax

本次小程序是基于 wepy 的,所以使用了优化后的 wepy.request;

基于 Egg.js 的版本。

可能与实际开发有点出入,适当修改。

import wepy from 'wepy';

export const HTTP_HOST = 'http://127.0.0.1:3000';

export const HTTP_HOST_API = `${HTTP_HOST}/api/wxmp`;

// cookie 的本地存储位置
const COOKIE_KEY = '__cookie_key__';
// csrfToken 的本地存储位置
const CSRF_TOKEN_KEY = '__csrf_token__';

/**
 * 清除用户Cookie
 */
export const cleanUserCookie = () => {
  wx.setStorageSync(COOKIE_KEY, '');
}

/**
 * 格式化用户需要的 cookie
 * @param {String} cookies
 */
export const normalizeUserCookie = (cookies = '') => {
  let __cookies = [];
  (cookies.match(/([\w\-.]*)=([^\s=]+);/g) || []).forEach((str) => {
    if (str !== 'path=/;' && str.indexOf('csrfToken=') !== 0) {
      __cookies.push(str);
    }
  });
  wx.setStorageSync(COOKIE_KEY, __cookies);
};

/**
 * 格式化 token
 */
const normalizeCsrfToken = () => {
  let __value = wx.getStorageSync(CSRF_TOKEN_KEY) || '';
  let __inputs = __value.match(/csrfToken=[\S]*/) || [];
  let __key = __inputs[0]; // csrfToken=1212132323;
  if (!!!__key) {
    return '';
  }
  // 脱水
  return __key.replace(/;$/, '').replace(/^csrfToken=/, '');
};

/**
 * 保存 csrf 的cookie
 * 不一定每次请求都会更新 cookie
 * @param {String} cookie
 */
const seveCsrfTokenCookie = (cookie) => {
  if (cookie) {
    wx.setStorageSync(CSRF_TOKEN_KEY, cookie);
  }
};

/**
 * 请求数据
 * @param {Object} opt
 */
export const doAjax = (opt) => {
  return new Promise((resolve, reject) => {
    let Cookies = wx.getStorageSync(COOKIE_KEY) || [];
    let csrf = normalizeCsrfToken();
    let url = opt.url;
    // 整理 Cookie
    Cookies.push(`csrfToken=${csrf};`);

    // 设置请求头部
    opt.header = Object.assign(
      {
        'x-csrf-token': csrf,
        Cookie: Cookies.join(' ')
      },
      opt.header || {}
    );
    opt.success = (data) => {
      seveCsrfTokenCookie(data.header['set-cookie']);
      // 统一操作
      if (data.statusCode == 200) {
        if (url === '/login') {
          normalizeUserCookie(data.header['set-cookie']);
        }
        resolve(data.data);
      } else {
        reject('未知错误,请重试一次');
      }
    };
    opt.fail = (err) => {
      reject(err);
    };
    opt.url = `${HTTP_HOST_API}${opt.url}`;
    wepy.request(opt);
  });
};

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
禁止刷新,回退的JS
Nov 25 Javascript
Javascript 两个窗体之间传值实现代码
Sep 25 Javascript
深入领悟JavaScript中的面向对象
Nov 18 Javascript
jQuery操作Select的Option上下移动及移除添加等等
Nov 18 Javascript
Dojo Javascript 编程规范 规范自己的JavaScript书写
Oct 26 Javascript
JavaScript实现开关等效果
Sep 08 Javascript
Vue前端开发规范整理(推荐)
Apr 23 Javascript
解决Layui数据表格中checkbox位置不居中的方法
Aug 15 Javascript
vue项目创建步骤及路由router
Jan 14 Javascript
详解使用mocha对webpack打包的项目进行"冒烟测试"的大致流程
Apr 27 Javascript
vue.js实现简单购物车功能
May 30 Javascript
vue 实现根据data中的属性值来设置不同的样式
Aug 04 Javascript
JS伪继承prototype实现方法示例
Jun 20 #Javascript
通过jquery.cookie.js实现记住用户名、密码登录功能
Jun 20 #jQuery
Vue.JS实现垂直方向展开、收缩不定高度模块的JS组件
Jun 19 #Javascript
Vue兼容ie9的问题全面解决方案
Jun 19 #Javascript
详解Vue-cli中的静态资源管理(src/assets和static/的区别)
Jun 19 #Javascript
vue-cli2.x项目优化之引入本地静态库文件的方法
Jun 19 #Javascript
vue异步加载高德地图的实现
Jun 19 #Javascript
You might like
php处理restful请求的路由类分享
2014/02/27 PHP
php smarty模板引擎的6个小技巧
2014/04/24 PHP
PHP中使用addslashes函数转义的安全性原理分析
2014/11/03 PHP
thinkphp文件引用与分支结构用法实例
2014/11/26 PHP
php遍历解析xml字符串的方法
2016/05/05 PHP
Laravel中七个非常有用但很少人知道的Carbon方法
2017/09/21 PHP
使一个函数作为另外一个函数的参数来运行的javascript代码
2007/08/13 Javascript
jquery实现的随机多彩tag标签随机颜色和字号大小效果
2014/03/27 Javascript
轻松创建nodejs服务器(6):作出响应
2014/12/18 NodeJs
js编写当天简单日历效果【实现代码】
2016/05/03 Javascript
BootStrap中的table实现数据填充与分页应用小结
2016/05/26 Javascript
JS简单获取当前年月日星期的方法示例
2017/02/07 Javascript
利用Chrome DevTools直接调试Node.js和JavaScript的方法详解(并行)
2017/02/16 Javascript
js鼠标移动时禁止选中文字
2017/02/19 Javascript
vue指令以及dom操作详解
2017/03/04 Javascript
JS实现图片预加载之无序预加载功能代码
2017/05/12 Javascript
vue-cli中的webpack配置详解
2017/09/25 Javascript
使用puppeteer破解极验的滑动验证码
2018/02/24 Javascript
初学node.js中实现删除用户路由
2019/05/27 Javascript
JS highcharts实现动态曲线代码示例
2020/10/16 Javascript
[47:03]完美世界DOTA2联赛PWL S3 Galaxy Racer vs Phoenix 第二场 12.10
2020/12/13 DOTA
python基础入门学习笔记(Python环境搭建)
2016/01/13 Python
Python爬虫实现网页信息抓取功能示例【URL与正则模块】
2017/05/18 Python
python向字符串中添加元素的实例方法
2019/06/28 Python
Flask项目中实现短信验证码和邮箱验证码功能
2019/12/05 Python
pytorch 获取tensor维度信息示例
2020/01/03 Python
基于python实现计算两组数据P值
2020/07/10 Python
基于Python实现下载网易音乐代码实例
2020/08/10 Python
最新销售员个人自荐信
2013/09/21 职场文书
工伤赔偿协议书范本
2014/04/15 职场文书
夫妻分居协议书范本(有子女版)
2014/11/01 职场文书
2014年林业工作总结
2014/12/05 职场文书
幼儿园老师新年寄语2015
2014/12/08 职场文书
医德医风学习心得体会
2016/01/25 职场文书
励志语录:只有自己足够强大,才能不被别人践踏
2020/01/09 职场文书
详解Python自动化之文件自动化处理
2021/06/21 Python