微信小程序模拟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 相关文章推荐
Jquery实现搜索框提示功能示例代码
Aug 13 Javascript
javascript实用小函数使用介绍
Nov 11 Javascript
Select标签下拉列表二级联动级联实例代码
Feb 07 Javascript
自定义函数实现IE7与IE8不兼容js中trim函数的问题
Feb 03 Javascript
JavaScript实现打字效果的方法
Jul 10 Javascript
简单了解JavaScript操作XPath的一些基本方法
Jun 03 Javascript
Document.body.scrollTop的值总为零的快速解决办法
Jun 09 Javascript
js改变透明度实现轮播图的算法
Aug 24 Javascript
AngularJS实现树形结构(ztree)菜单示例代码
Sep 18 Javascript
JS设计模式之惰性模式(二)
Sep 29 Javascript
JavaScript中click和onclick本质区别与用法分析
Jun 07 Javascript
keep-alive不能缓存多层级路由菜单问题解决
Mar 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
雄兵连三大错觉:凯莎没了,凉冰阵亡了,华烨觉得自己又行了
2020/04/09 国漫
dedecms 批量提取第一张图片最为缩略图的代码(文章+软件)
2009/10/29 PHP
[原创]php简单隔行变色功能实现代码
2016/07/09 PHP
JavaScript 脚本将当地时间转换成其它时区
2009/03/19 Javascript
原生Js与jquery的多组处理, 仅展开一个区块的折叠效果
2011/01/09 Javascript
关于锚点跳转及jQuery下相关操作与插件
2012/10/01 Javascript
怎样在JavaScript里写一个swing把数据插入数据库
2012/12/10 Javascript
angular分页指令操作
2017/01/09 Javascript
react native带索引的城市列表组件的实例代码
2017/08/08 Javascript
详解angular路由高亮之RouterLinkActive
2018/04/28 Javascript
vue-router路由懒加载的实现(解决vue项目首次加载慢)
2018/08/28 Javascript
Vue实现移动端页面切换效果【推荐】
2018/11/13 Javascript
javascript实现日历效果
2019/06/17 Javascript
微信小程序文章列表功能完整实例
2020/06/03 Javascript
Python中设置变量作为默认值时容易遇到的错误
2015/04/03 Python
Python使用scrapy抓取网站sitemap信息的方法
2015/04/08 Python
简述:我为什么选择Python而不是Matlab和R语言
2017/11/14 Python
详解python--模拟轮盘抽奖游戏
2019/04/12 Python
python中字典按键或键值排序的实现代码
2019/08/27 Python
Python多线程爬取豆瓣影评API接口
2019/10/22 Python
python GUI库图形界面开发之PyQt5表单布局控件QFormLayout详细使用方法与实例
2020/03/06 Python
Keras 利用sklearn的ROC-AUC建立评价函数详解
2020/06/15 Python
Python自动创建Excel并获取内容
2020/09/16 Python
HTML5 虚拟键盘出现挡住输入框的解决办法
2017/02/14 HTML / CSS
高中三年学习生活的自我评价
2013/10/10 职场文书
理工大学毕业生自荐信
2013/11/01 职场文书
银行柜员应聘推荐信范文
2013/11/24 职场文书
春节活动策划方案
2014/01/24 职场文书
数学高效课堂实施方案
2014/03/29 职场文书
股票投资建议书
2014/05/19 职场文书
机械工程及自动化专业求职信
2014/09/03 职场文书
2014乡镇党政班子四风问题思想汇报
2014/09/14 职场文书
2015年医院创卫工作总结
2015/04/22 职场文书
改进工作作风心得体会
2016/01/23 职场文书
python可视化大屏库big_screen示例详解
2021/11/23 Python
Python实战实现爬取天气数据并完成可视化分析详解
2022/06/16 Python