微信小程序模拟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 相关文章推荐
使用正则替换变量
May 05 Javascript
弹出层之1:JQuery.Boxy (一) 使用介绍
Oct 06 Javascript
js对图片base64编码字符串进行解码并输出图像示例
Mar 17 Javascript
jquery css 设置table的奇偶行背景色示例
Jun 03 Javascript
Bootstrap弹出带合法性检查的登录框实例代码【推荐】
Jun 23 Javascript
详解vue-validator(vue验证器)
Jan 16 Javascript
jQuery滚动监听实现商城楼梯式导航效果
Mar 06 Javascript
Angular.js中下拉框实现渲染html的方法
Jun 18 Javascript
bootstrap 弹出框modal添加垂直方向滚轴效果
Jul 09 Javascript
Vue数据绑定实例写法
Aug 06 Javascript
Vue使用轮询定时发送请求代码
Aug 10 Javascript
js节流防抖应用场景,以及在vue中节流防抖的具体实现操作
Sep 21 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
IIS php环境配置PHP5 MySQL5 ZendOptimizer phpmyadmin安装与配置
2008/11/18 PHP
3个PHP多维数组转为一维数组的方法实例
2014/03/13 PHP
PHP实现图片自动清理的方法
2015/07/08 PHP
Laravel向公共模板赋值方法总结
2019/06/25 PHP
安装PHP扩展时解压官方 tgz 文件后没有configure文件无法进行配置编译的问题
2020/08/26 PHP
Mootools 1.2教程 Fx.Tween的使用
2009/09/15 Javascript
js打印纸函数代码(递归)
2010/06/18 Javascript
jquery判断字符输入个数(数字英文长度记为1,中文记为2,超过长度自动截取)
2010/10/15 Javascript
在父页面调用子页面的JS方法
2013/09/29 Javascript
JavaScript 判断用户输入的邮箱及手机格式是否正确
2013/12/08 Javascript
JavaScript DOM操作表格及样式
2015/04/13 Javascript
JavaScript中使用concat()方法拼接字符串的教程
2015/06/06 Javascript
使用AngularJS中的SCE来防止XSS攻击的方法
2015/06/18 Javascript
纯javascript判断查询日期是否为有效日期
2015/08/24 Javascript
AngularJS+Node.js实现在线聊天室
2015/08/28 Javascript
angularjs表格分页功能详解
2016/01/21 Javascript
jquery zTree异步加载、模糊搜索简单实例分享
2016/03/24 Javascript
jquery计算出left和top,让一个div水平垂直居中的简单实例
2016/07/13 Javascript
jquery dataTable 获取某行数据
2017/05/05 jQuery
Vue 多层组件嵌套二种实现方式(测试实例)
2017/09/08 Javascript
vue.js模仿京东省市区三级联动的选择组件实例代码
2017/11/22 Javascript
angularjs实现柱状图动态加载的示例
2017/12/11 Javascript
js判断节假日实例代码
2017/12/27 Javascript
jq.ajax+php+mysql实现关键字模糊查询(示例讲解)
2018/01/02 Javascript
Babel 入门教程学习笔记
2018/06/13 Javascript
JS数组求和的常用方法实例小结
2019/01/07 Javascript
Python将多份excel表格整理成一份表格
2018/01/03 Python
python sorted方法和列表使用解析
2019/11/18 Python
教你一分钟在win10终端成功安装Pytorch的方法步骤
2021/01/28 Python
HTML5 video 视频标签使用介绍
2014/02/03 HTML / CSS
师德个人剖析材料
2014/02/02 职场文书
2015年艾滋病宣传活动总结
2015/03/27 职场文书
用人单位的规章制度,怎样制定才是有效的?
2019/07/09 职场文书
Redis的字符串是如何实现的
2021/10/24 Redis
vue的项目如何打包上线
2022/04/13 Vue.js
MySQL提取JSON字段数据实现查询
2022/04/22 MySQL