微信小程序模拟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 控制小数位数的实现代码
Aug 02 Javascript
50款非常棒的 jQuery 插件分享
Mar 29 Javascript
通过遮罩层实现浮层DIV登录的js代码
Feb 07 Javascript
JavaScript获取网页表单提交方式的方法
Apr 02 Javascript
JavaScript+html5 canvas绘制的圆弧荡秋千效果完整实例
Jan 26 Javascript
jQuery实现滚动鼠标放大缩小图片的方法(附demo源码下载)
Mar 05 Javascript
在AngularJS中使用jQuery的zTree插件的方法
Apr 21 Javascript
Vue服务端渲染和Vue浏览器端渲染的性能对比(实例PK )
Mar 31 Javascript
js实现分页功能
May 24 Javascript
vue实现拖拽的简单案例 不超出可视区域
Jul 25 Javascript
vue中使用elementUI组件手动上传图片功能
Dec 13 Javascript
uniapp开发小程序实现滑动页面控制元素的显示和隐藏效果
Dec 10 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 zlib压缩和解压缩swf文件的代码
2008/12/30 PHP
php使用json_encode对变量json编码
2014/04/07 PHP
简介PHP的Yii框架中缓存的一些高级用法
2016/03/29 PHP
JavaScript 对象成员的可见性说明
2009/10/16 Javascript
IE6,IE7,IE8下使用Javascript记录光标选中范围(已补全)
2011/08/28 Javascript
JS+CSS实现一个气泡提示框
2013/08/18 Javascript
js实现倒计时时钟的示例代码
2013/12/17 Javascript
Extjs根据条件设置表格某行背景色示例
2014/07/23 Javascript
每天一篇javascript学习小结(Function对象)
2015/11/16 Javascript
在Mac OS上安装使用Node.js的项目自动化构建工具Gulp
2016/06/18 Javascript
Jquery AJAX POST与GET之间的区别详细介绍
2016/10/17 Javascript
vue-hook-form使用详解
2017/04/07 Javascript
基于react框架使用的一些细节要点的思考
2017/05/31 Javascript
JS散列表碰撞处理、开链法、HashTable散列示例
2019/02/08 Javascript
nodejs中各种加密算法的实现详解
2019/07/11 NodeJs
js实现拖拽元素选择和删除
2020/08/25 Javascript
在vue中动态修改css其中一个属性值操作
2020/12/07 Vue.js
修改NPM全局模式的默认安装路径的方法
2020/12/15 Javascript
[01:12:35]Spirit vs Navi Supermajor小组赛 A组败者组第一轮 BO3 第二场 6.2
2018/06/03 DOTA
Python的ORM框架SQLObject入门实例
2014/04/28 Python
教你使用python画一朵花送女朋友
2018/03/29 Python
基于Python Numpy的数组array和矩阵matrix详解
2018/04/04 Python
python 获取字符串MD5值方法
2018/05/29 Python
Python 进程之间共享数据(全局变量)的方法
2019/07/16 Python
Pytorch 多维数组运算过程的索引处理方式
2019/12/27 Python
Pycharm中import torch报错的快速解决方法
2020/03/05 Python
基于Tensorflow读取MNIST数据集时网络超时的解决方式
2020/06/22 Python
python cookie反爬处理的实现
2020/11/01 Python
食品安全工作方案
2014/05/07 职场文书
团队精神口号
2014/06/06 职场文书
5种 JavaScript 方式实现数组扁平化
2021/10/05 Javascript
Vue的过滤器你真了解吗
2022/02/24 Vue.js
【海涛DOTA解说】EVE女子战队独家录像加ZSMJ神牛两连发
2022/04/01 DOTA
spring IOC容器的Bean管理XML自动装配过程
2022/05/30 Java/Android
Android中View.post和Handler.post的关系
2022/06/05 Java/Android
关于对TypeScript泛型参数的默认值理解
2022/07/15 Javascript