微信小程序模拟 cookie

开发背景

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

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

模拟 cookie

经过浏览器的开发工具,Network 栏查看请求,浏览器中的 cookie 会携带在每一个 httpRequest Headers 里面,用 Cookie 做为键名。小程序

那么,在微信官方请求方式 wx.request 中,咱们设置 header,添加一个 Cookie 应该能够得以模拟。微信小程序

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

经过登陆接口(登陆的时候,服务器端会植入 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。当约定位置带上的 csrfTokencookie 里面的 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);
    });
};

原文阅读:微信小程序模拟 cookie

相关文章
相关标签/搜索