热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

微信小程序模拟cookie的实现

本篇文章主要介绍了微信小程序模拟cookie的实现,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

开发背景

现有系统已经有一套完整的接口,用户状态、验证都是基于 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);
  });
};

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


推荐阅读
author-avatar
洪锐林
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有