微信小程序模擬cookie的實現(xiàn)
大部分的微信小程序不支持cookie,那么微信小程序模擬cookie的實現(xiàn)要如何開發(fā),下面為大家?guī)砦⑿判〕绦蚰Mcookie的實現(xiàn)過程。

開發(fā)背景
現(xiàn)有系統(tǒng)已經(jīng)有一套完整的接口,用戶狀態(tài)、驗證都是基于 cookie 的。
部分業(yè)務(wù)要上小程序版本,眾所周知,微信小程序不支持 cookie 的。要上線的業(yè)務(wù),最好的方式還是基于現(xiàn)有這套接口做,改動不大,也最快。
模擬 cookie
通過瀏覽器的開發(fā)工具,Network 欄查看請求,瀏覽器中的 cookie 會攜帶在每個 http 的 Request Headers 里面,用 Cookie 作為鍵名。
那么,在微信官方請求方式 wx.request 中,我們設(shè)置 header,添加一個 Cookie 應(yīng)該可以得以模擬。
問題又來了,怎么獲取到服務(wù)器返回的 cookie 呢。
通過登錄接口(登錄的時候,服務(wù)器端會植入 cookie 作為 session),查看 http 返回頭。
wx.request({
url: '/api/login',
success: (data) => {
if(data.statusCode === 200) {
console.log(data);
// data 中應(yīng)該會有 Set-Cookie 或 set-cookie 的字樣,嗯,那就是服務(wù)器種下的 cookie
}
}
})
拿到 cookie 存入本地中,下次請求數(shù)據(jù)的時候直接塞進去,完美。
格式化 cookie
原本以為 cookie 只需要一進一出就可以完美模擬,實際操作才發(fā)現(xiàn),攜帶上去的 cookie 服務(wù)器無法識別。
服務(wù)器返回的 cookie 中,會攜帶上很多儲存用的字段,例如 path=/;
// 服務(wù)器放回的 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=/; 在某些應(yīng)用下會是 path=/;
normalizeUserCookie 主要是過濾了 xx=xxx; 這樣的數(shù)據(jù),然后排除 path=/; 這樣無意義的數(shù)據(jù)。
在登錄接口的時候,存上 cookie,在接下來的請求中帶上,那么,應(yīng)該、沒錯、可能、可以模擬了。
配合 Egg.js
Egg 內(nèi)置的 egg-security 插件默認對所有『非安全』的方法,例如 POST,PUT,DELETE 都進行 CSRF 校驗。
Egg.js 雖然可以在配置中關(guān)閉 CSRF,但是,如果一定要使用呢?
首先,要弄明白一件事,csrfToken 怎么來的。
經(jīng)過多次驗證得知,當 http 請求時,在約定位置沒有攜帶上 csrfToken 值,此次請求會在返回的 cookie 中攜帶上一個新的 csrfToken;當本次請求已攜帶上值,就不會產(chǎn)生成 csrfToken。當約定位置帶上的 csrfToken 與 cookie 里面的 csrfToken 一致時,通過驗證。
接上面的 格式化用戶需要的 cookie 操作,先拋開 csrfToken 單獨處理用戶狀態(tài)等。
在每次請求結(jié)束后,試著單獨拿 cookie 中可能存在的 csrfToken,有值就緩存,沒值跳過用舊值。
封裝一個 Ajax
本次小程序是基于 wepy 的,所以使用了優(yōu)化后的 wepy.request;
基于 Egg.js 的版本。
可能與實際開發(fā)有點出入,適當修改。
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);
}
};
/**
* 請求數(shù)據(jù)
* @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};`);
// 設(shè)置請求頭部
opt.header = Object.assign(
{
'x-csrf-token': csrf,
Cookie: Cookies.join(' ')
},
opt.header || {}
);
opt.success = (data) => {
seveCsrfTokenCookie(data.header['set-cookie']);
// 統(tǒng)一操作
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);
});
};

立即掃碼關(guān)注

多門店/直營/加盟連鎖管理系統(tǒng)