微信小程序模拟cookie的实现

开发背景

现有系统已经有一套完整的接口,用户状态、验证都是基于 cookie 的。

部分业务要上小程序版本,众所周知,微信小程序不支持 cookie 的。要上线的业务,最好的方式还是基于现有这套接口做,改动不大,也最快。

模拟 cookie

通过浏览器的开发工具,Network 栏查看请求,浏览器中的 cookie 会携带在每个 http 的 Request Headers 里面,用 Cookie 作为键名。

那么,在微信官方请求方式 wx.request 中,我们设置 header,添加一个 Cookie 应该可以得以模拟。

问题又来了,怎么获取到服务器返回的 cookie 呢。

通过登录接口(登录的时候,服务器端会植入 cookie 作为 session),查看 http 返回头。

{ if(data.statusCode === 200) { console.log(data); // data 中应该会有 Set-Cookie 或 set-cookie 的字样,嗯,那就是服务器种下的 cookie } } })

拿到 cookie 存入本地中,下次请求数据的时候直接塞进去,完美。

格式化 cookie

原本以为 cookie 只需要一进一出就可以完美模拟,实际操作才发现,携带上去的 cookie 服务器无法识别。

服务器返回的 cookie 中,会携带上很多储存用的字段,例如 path=/;

rush:js;"> // 服务器放回的 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,userName=111111; Path=/; Expires=Thu,imgurl=; Path=/; Expires=Thu,21 Jun 2018 13:15:08 GMT';

// 模拟的是需要的格式样式
let virtualCookie = 'userKey=1234567890; userName=111111; userId=111;';

妈耶~要怎么过滤呢。

简单粗糙的写了一个过滤方案。

/**

  • 格式化用户需要的 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 的版本。

可能与实际开发有点出入,适当修改

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(cs<a href="https://www.jb51.cc/tag/rft/" target="_blank" class="keywords">rft</a>oken=${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);
    });
    };

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

相关文章

开发微信小程序的用户授权登录功能
小程序开发页面如何实现跳转?
浅谈小程序开发中蓝牙连接错误分析及解决方法
什么是小程序?它有哪些功能?
如何配置小程序开发项目结构?(教程)
怎么把自己的店加入小程序