微信小程序模拟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 相关文章推荐
W3C Group的JavaScript1.8 新特性介绍
May 19 Javascript
JS阻止冒泡事件以及默认事件发生的简单方法
Jan 17 Javascript
js+css实现导航效果实例
Feb 10 Javascript
详谈js中window.location.search的用法和作用
Feb 13 Javascript
详解使用fetch发送post请求时的参数处理
Apr 05 Javascript
Node.js  REPL (交互式解释器)实例详解
Aug 06 Javascript
express框架实现基于Websocket建立的简易聊天室
Aug 10 Javascript
详解React项目如何修改打包地址(编译输出文件地址)
Mar 21 Javascript
ES6小技巧之代替lodash
Jun 07 Javascript
Vue中遍历数组的新方法实例详解
Jul 21 Javascript
JavaScript函数IIFE使用详解
Oct 21 Javascript
JavaScript通如何过RGraph实现动态仪表盘
Oct 15 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-fpm的两种进程管理模式详解
2013/06/03 PHP
初识laravel5
2015/03/02 PHP
php关键字仅替换一次的实现函数
2015/10/29 PHP
PHP快速排序quicksort实例详解
2016/09/28 PHP
jQuery 可以拖动的div实现代码 脚本之家修正版
2009/06/26 Javascript
JavaScript CSS修改学习第二章 样式
2010/02/19 Javascript
document.all的一个比较完整的总结及案例
2013/01/31 Javascript
Js nodeType 属性全面解析
2013/11/14 Javascript
深入分析javascript中console命令
2016/08/14 Javascript
Javascript json object 与string 相互转换的简单实现
2016/09/27 Javascript
jQuery实现二维码扫描功能
2017/01/09 Javascript
jQuery插件FusionCharts实现的3D柱状图效果实例【附demo源码下载】
2017/03/03 Javascript
详解React中的组件通信问题
2017/07/31 Javascript
Javascript中 toFixed四舍六入方法
2017/08/21 Javascript
vue 下列表侧滑操作实例代码详解
2018/07/24 Javascript
Promise.all中对于reject的处理方法
2018/08/01 Javascript
angularjs模态框的使用代码实例
2019/12/20 Javascript
Pyhthon中使用compileall模块编译源文件为pyc文件
2015/04/28 Python
Python调用命令行进度条的方法
2015/05/05 Python
python动态性强类型用法实例
2015/05/09 Python
Python实现复杂对象转JSON的方法示例
2017/06/22 Python
图解Python变量与赋值
2018/04/03 Python
python 实现图片旋转 上下左右 180度旋转的示例
2019/01/24 Python
python使用百度文字识别功能方法详解
2019/07/23 Python
pandas中DataFrame修改index、columns名的方法示例
2019/08/02 Python
python如何构建mock接口服务
2021/01/28 Python
日本高端护肤品牌:Tatcha
2016/08/29 全球购物
日本著名化妆品零售网站:Cosme Land
2019/03/01 全球购物
explicit和implicit的含义
2012/11/15 面试题
高考自主招生自荐信
2013/10/20 职场文书
父母对孩子说的话
2014/04/12 职场文书
《从现在开始》教学反思
2014/04/15 职场文书
单身证明格式样本
2015/06/15 职场文书
家长会感言
2015/08/01 职场文书
python第三方网页解析器 lxml 扩展库与 xpath 的使用方法
2021/04/06 Python
Python爬虫基础讲解之请求
2021/05/13 Python