京东购物小程序cookie方案实践
早期为了解决“会话保持”的需求,社区中出现了「cookie方案」并最终成为W3C标准:当某个网站登录成功后,客户端(浏览器)收到一个cookie标识(文本)并保存下来,在后续请求中会自动带上这个字段,由此Web后台可以判断是否同一个用户,从而使“会话”得以延续。 微信小程序没有像浏览器一样内置实现了cookie方案,需要开发者自行模拟,而原先京东购物小程序及京喜小程序(现微信一级购物入口)是从微信及手Q购物H5中迁移迭代出来的,也就是说我们不仅要在小程序中模拟一套cookie方案,并且要保持和原业务对cookie处理逻辑的一致,为此我们将实现方向确定为“基于小程序开放能力,和浏览器保持一致”。 微信小程序开放了 数据缓存 Storage 和 网络 Network 这两种能力,通过这两套API,我们可以自行DIY一个cookie方案。 PS:本文所有代码及使用示例都可以 在这里 找到,阅读本文时配合实践,效果更佳。 二、浏览器中的cookie为了保持后端对cookie的处理逻辑和原来的H5一致,小程序的实现需要往浏览器看齐。 所以模拟小程序的cookie前,先看看浏览器的cookie机制,主要有以下几个部分:
在浏览器的
(此图片来源于网络,如有侵权,请联系删除! ) 三、小程序中的cookie实现方案设计在小程序中模拟Cookie,主要涉及五个部分:
(此图片来源于网络,如有侵权,请联系删除! ) 其中我们会重点关注 「Cookie基础库」 的实现,另外也会给出「Request基础库」的封装示例。 本地存储 小程序提供了 「数据缓存 Storage API」(可以理解为Web规范中的 我们可以利用这些API,在Storage中新开一个 // 存: wx.setStorageSync('cookies', cookies) // 取: wx.getStorageSync('cookies') 复制代码 其中 // cookies = { cookie1: { // “最小cookie单元” ==> cookieItem name: 'cookie1', // cookie名 value: 'xxx', // cookie值 expires: 'Fri, 17 Jan 2020 08:49:41 GMT' // 过期时间,使用GMT(格林威治标准时间)格式 } }, 复制代码 上面的 打开【微信开发工具】的
(此图片来源于网络,如有侵权,请联系删除! ) 读写操作这部分主要作为“公共基础库“的角色,为外部业务提供增删改查cookie的API。 1. 获取cookie———— 步骤:从Storage中取出完整cookies ==> 取出指定name的cookie项 ==> 校验有效期 ==> 返回值value 实现如下: function getCookie(name = '') { let cookies = wx.getStorageSync('cookies') // try/catch 略过 let { value, expires } = cookies[name] || {} return (name && expires && !isExpired(expires)) ? decodeURIComponent(cookieItem.value) : '' } 复制代码 2. 设置cookie———— 步骤:从Storage中取出完整cookies ==> 解析入参 ==> 覆盖更新 ==> 同步到本地Storage 首先看下本API设计需求:
调用示例如下: setCookie({ cookie1: 12345, cookie2: '12345' }) setCookie({ cookie1: { value: 12345, maxAge: 3600 * 24 // 自定义有效期(这里示例是24小时) }, cookie2: { value: '12345', expires: 'Wed, 21 Oct 2015 07:28:00 GMT' // 标准GMT格式 } }) 复制代码 这里可对入参遍历,而cookie子项无论直接传值value还是传了详细object,都尽量的获取 function setCookie(cookiesParam) { let oldCookies = wx.getStorageSync('cookies') // try/catch 略过 let newCookies = {} // 由 cookiesParam 转化为标准格式后的cookies for (let name in cookiesParam) { if (isObject(cookiesParam[name])) { // 传入是Object格式 let { value, expires, maxAge } = cookiesParam[name] // 转换为标准cookie格式(cookieItem) newCookies[name] = getStandardCookieItem({ name, value, expires, maxAge }) } else { newCookies[name] = getStandardCookieItem({ name, value: cookiesParam[name] }) } } // 同步到本地Storage saveCookiesToStorage(Object.assign({}, oldCookies, newCookies)) } 复制代码 3. 删除cookie———— 步骤:从Storage中取出完整cookies ==> 删除指定的cookie项 ==> 同步到本地Storage function removeCookie(cookieName) { let cookies = wx.getStorageSync('cookies') // try/catch 略过 delete cookies[cookieName] saveCookiesToStorage(Object.assign({}, cookies)) } 复制代码 四、Cookie 在网络中的传递本节主要简单实现设计图中的【Request基础库】部分
(此图片来源于网络,如有侵权,请联系删除! ) 如上图所示,Cookie在网络中的传输主要有四个过程:
Set-Cookie
Cookie
Cookie
以下是对一个请求的抓包示例:
(此图片来源于网络,如有侵权,请联系删除! ) 在小程序中,请求发起有两种方式: function requestPro({ url, data, header, method = 'GET' }) { return new Promise((resolve, reject) => { wx.request({ url, data, header: Object.assign({}, { 'Cookie': CookieLib.getCookiesStr() }, header), // 请求头————带上Cookie success (res) { let { data : resData, header, statusCode } = res let setCookieStr = header['Set-Cookie'] || header['set-cookie'] || '' CookieLib.setCookieFromHeader(setCookieStr) // 响应头————解析Set-Cookie resolve(resData) }, fail (err) { reject(err) } }) }) } 复制代码 如上代码所示,Cookie在前端侧请求模块中的处理主要有3点: 1. 请求携带 步骤:(每次发请求前)从Storage中取出完整cookies ==> 转化为HTTP规范的请求头Cookie格式 ==> 设置到 上面代码中的 2. 响应设置 步骤:(每次收到响应后)解析 这里处理 具体实现可在文末Demo中找到。 3. 编码问题「Cookie值编码方式」是容易产生困惑的地方,目前看到的广泛做法都是使用「URL编码」。 但笔者翻阅 RFC6265 发现,原始规范中并没有对编码进行指定,比如在第四章 Server Requirements (服务端)中是这样描述: To maximize compatibility with user agents, servers that wish to store arbitrary data in a cookie-value SHOULD encode that data, for example, using Base64 [RFC4648]. “为了最好的兼容效果,服务端应该对cookie值进行编码,例如使用Base64。” 而在第五章 User Agent Requirements (客户端,也就是浏览器),则是“建议以第四章服务端的实现为准”。 总之规范并没有指定使用「URL编码」,但基于该编码方案已经深入人心,也就顺其自然成了“默认选择”。那这里也不做例外,浏览器怎么做,咋们小程序也保持一致。 在浏览器中,推荐cookie值经过 而对于响应头 五、性能优化(高频读写)前面实现中每次读写cookie都会调用小程序Storage API(而且是同步的),小程序框架会读写到本地Storage。 对于高频场景,可以将cookie在内存中维护一份,读写都直接走「内存层」,有更新才同步到「Storage层」。 1. 初始化 首先需要在内存中声明一个 2. 读 前面初始化时已经从Storage读取一次cookies,后续getCookie就直接读内存的 3. 写写操作直接更新内存,间接更新Storage。 如果有高频写场景,可以考虑做个任务队列进行节流。 六、单元测试 微信官方在2019年5月推出了「小程序自动化 SDK」 在购物小程序场景试用了一下,cookie相关的用例很快就完成了,简直是开发者的福音:真香!!!
(此图片来源于网络,如有侵权,请联系删除! ) 实际项目中,对cookie的单元测试可以分为两类:
以验证 it('API验证:setCookie()', async () => { await miniProgram.evaluate(() => { wx.CookieLib.setCookie({ // 调用API cookie1: 12345, }) }) let { cookies } = await miniProgram.callWxMethod('getStorageSync', 'cookies') expect(cookies['cookie1'].value).toBe(12345) // 期望成功设置cookie1为12345 }) 复制代码 这里为了方便测试用例调用基础库API,在小程序启动前,把Cookie基础库(CookieLib)挂到了 fs.appendFileSync('./your_project/app.js', ''\n wx.CookieUtil = require(\'./lib/cookie.js\');\n'') 复制代码 七、Cookie安全Cookie安全是一个比较大的话题,这里只简单列出和小程序相关的几个点。 path、domin、HttpOnly、Secure、SameSite 小程序中已经做了一些安全措施,比如只能走HTTPS、合法域名需要管理员到微信后台进行配置、Storage只能由写入它的小程序中访问,等等。 因此 白名单机制
前端防篡改 小程序前端更多是防“误改”————即在操作Cookie过程中,发生了意料之外的修改。通常发生在JS“引用拷贝”特性上,比如前面提到的内存维护一个 SessionSession机制将用户状态放在了服务端维护,具备更好的安全性,而且目前各种后端对于session的存储和同步都有很成熟的技术方案,有条件的业务应以Session为主做会话保持。 指纹上报用户访问时生成设备指纹并上报(通常是登录/结算等环节),业务后台配合风控系统,遇到异常请求时下发验证环节。 八、完整小程序实现Demo代码片段: developers.weixin.qq.com/s/x4sFASmh7…
(此图片来源于网络,如有侵权,请联系删除! ) 九、小结本文先解析了浏览器的 Cookie机制 运作原理,然后使用「数据缓存」和「网络」能力,以 公共基础库 的形式,在小程序中实现了一套 Cookie方案。希望对大家有所帮助。 |
免责声明:本站所有文章和图片均来自用户分享和网络收集,文章和图片版权归原作者及原出处所有,仅供学习与参考,请勿用于商业用途,如果损害了您的权利,请联系网站客服处理。
本站为避免不必要的纷争,分享的所有资源中一切可能有版权风险的资源将全部转载自第三方网站或平台,站长只为大家提供相关资源的介绍和跳转引导。 因可能有疏忽大意,所以如有遗漏资源侵犯了您的合法权利,请联系站长删除。
【小程序源码网资源下载使用说明】:
本站所分享的一切QQ小程序源码,thinkphp整站源码,微信小程序源码,图文教程等资源仅供用户学习参考使用,任何人不得作其他用途,违者自行承担所有责任。
【小程序源码网毫无人看的介绍】:
本站又称Z站,原名贼娘网,开站于2018年,换过三任站长,目前站长是第四任站长,本站是一个主要分享免费开源小程序源码/网站源码/免费素材/教程资源的网站,主要小程序资源有用于学习的小程序源码,也有正版原创可商用的小程序源码,是一个公益博客型网站。
【小程序源码网原创源码版权申明】:
未经小程序源码网许可,任何人不得擅自使用本站原创首发源码进行商业行为(除本站VIP用户在期限内,版权无使用限制),否则将依法承担相应赔偿责任。
【小程序源码网转载文章版权申明】:
本站所转载的QQ小程序或微信小程序源码与其他资源仅供学习,任何人不得作其他用途,违者自行承担所有责任。
【小程序源码网站长最后的屁话】:
如有您认为本站有任何侵犯您合法权益的文章,或者您有什么疑问需求,欢迎联系站长QQ,站长24小时在线,备注公司名称和源码版权问题或者需要小程序定制开发等站长业务类型可急速处理,如果您只是交流小程序的一些开发问题或源码问题可以加入QQ群讨论,就不用加站长啦,对于白嫖党,QQ群才是处理问题的天堂,当然站长也欢迎大家骚扰~
小程序源码网 » 京东购物小程序cookie方案实践