京东购物小程序cookie方案实践

早期为了解决“会话保持”的需求,社区中出现了「cookie方案」并最终成为W3C标准:当某个网站登录成功后,客户端(浏览器)收到一个cookie标识(文本)并保存下来,在后续请求中会自动带上这个字段,由此Web后台可以判断是否同一个用户,从而使“会话”得以延续。

微信小程序没有像浏览器一样内置实现了cookie方案,需要开发者自行模拟,而原先京东购物小程序及京喜小程序(现微信一级购物入口)是从微信及手Q购物H5中迁移迭代出来的,也就是说我们不仅要在小程序中模拟一套cookie方案,并且要保持和原业务对cookie处理逻辑的一致,为此我们将实现方向确定为“基于小程序开放能力,和浏览器保持一致”。

微信小程序开放了 数据缓存 Storage 和 网络 Network 这两种能力,通过这两套API,我们可以自行DIY一个cookie方案。

PS:本文所有代码及使用示例都可以 在这里 找到,阅读本文时配合实践,效果更佳。

二、浏览器中的cookie

为了保持后端对cookie的处理逻辑和原来的H5一致,小程序的实现需要往浏览器看齐。

所以模拟小程序的cookie前,先看看浏览器的cookie机制,主要有以下几个部分:

  • 本地存储:浏览器会在本地分配一块空间,存储cookie
  • 请求携带:每次发起请求,都会从本地取出cookie并追加在请求头上
  • 响应设置:当响应头有Set-Cookie字段时,需要解析并更新
  • 过期时间:每个cookie字段有单独的过期时间,并且到期会自动清除
  • 读写操作:暴露API给前端JS调用,可进行增删改查操作
  • 作用域:路径path、域名domin
  • 编码:cookie值,在网络传输需要encode,建议存储也一样
  • 其它:HttpOnly、Secure、SameSite

在浏览器的 DevTools 中,可以看到当前站点下的Cookie明细:

三、小程序中的cookie实现

方案设计

在小程序中模拟Cookie,主要涉及五个部分:

其中我们会重点关注 「Cookie基础库」 的实现,另外也会给出「Request基础库」的封装示例。

本地存储

小程序提供了 「数据缓存 Storage API」(可以理解为Web规范中的 LocalStorage ),支持存储“原生类型、Date、及能够通过JSON.stringify序列化的对象”。

我们可以利用这些API,在Storage中新开一个 cookies 字段进行存储:

// 存:
wx.setStorageSync('cookies',cookies)
// 取:
.getStorageSync('cookies')
复制代码

其中 cookies 的「存储结构」如下:

,// cookie名
        value: 'xxx',136);">// cookie值
        expires: 'Fri,17 Jan 2020 08:49:41 GMT' // 过期时间,使用GMT(格林威治标准时间)格式
    }
},复制代码

上面的 cookie1 便是一个“最小cookie单元 cookieItem ”,包含了3个字段(name、value、expires),是本文中定义的「标准cookie格式」,也是cookie操作的基本单元。

打开【微信开发工具】的 Storage 选项卡,可以查看本地存储的情况:

读写操作

这部分主要作为“公共基础库“的角色,为外部业务提供增删改查cookie的API。

1. 获取cookie———— getCookie()

步骤:从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———— setCookie()

步骤:从Storage中取出完整cookies ==> 解析入参 ==> 覆盖更新 ==> 同步到本地Storage

首先看下本API设计需求:

  • 设置单个/多个cookie
  • 直接传值/传cookieItem(Object)
  • 时间格式maxAge/expires

调用示例如下:

setCookie({
    cookie1: 12345,cookie2: '12345'
})

cookie1: {
        value: maxAge: 3600 * 24  // 自定义有效期(这里示例是24小时)
    },51); font-weight: 700;">cookie2: {
        '12345',0);">'Wed,21 Oct 2015 07:28:00 GMT' // 标准GMT格式
    }
})
复制代码

这里可对入参遍历,而cookie子项无论直接传值value还是传了详细object,都尽量的获取 name/value/expires/maxAge ,传给格式化函数转为标准的 cookieItem :

3. 删除cookie———— removeCookie()

步骤:从Storage中取出完整cookies ==> 删除指定的cookie项 ==> 同步到本地Storage

四、Cookie 在网络中的传递

本节主要简单实现设计图中的【Request基础库】部分

如上图所示,Cookie在网络中的传输主要有四个过程:

Set-Cookie
Cookie
Cookie

以下是对一个请求的抓包示例:

在小程序中,请求发起有两种方式: HTTP 和 WebSocket ,这里以HTTP为例,先对请求api进行「封装」:

 }) {
    return new Promise((resolve,reject) => {
        wx.request({
            url,header: 'Cookie': CookieLib.getCookiesStr() },header),136);">// 请求头————带上Cookie
            success (res) {
              let { data : resData,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格式 ==> 设置到 Request Header 中

上面代码中的 getCookiesStr() 直接取cookies拼接即可,返回示例: cookie1=xxx;cookie2=yyy 。

2. 响应设置

步骤:(每次收到响应后)解析 Response Header 的 Set-Cookie 字段 ==> 转为标准Cookie格式 ==> setCookie()

这里处理 Set-Cookie 内容时,有几个点需要留意: - 最基本的格式: Set-Cookie: <cookie-name>=<cookie-value> - 可能同时包含多个cookie字段,以,分割(但需要排除时间值里的,) - 时间格式:Max-Age/Expires (不区分大小写)

具体实现可在文末Demo中找到。

3. 编码问题

「Cookie值编码方式」是容易产生困惑的地方,目前看到的广泛做法都是使用「URL编码」。

但笔者翻阅 RFC6265 发现,原始规范中并没有对编码进行指定,比如在第四章 Server Requirements (服务端)中是这样描述:

“为了最好的兼容效果,服务端应该对cookie值进行编码,例如使用Base64。”

而在第五章 User Agent Requirements (客户端,也就是浏览器),则是“建议以第四章服务端的实现为准”。

总之规范并没有指定使用「URL编码」,但基于该编码方案已经深入人心,也就顺其自然成了“默认选择”。那这里也不做例外,浏览器怎么做,咋们小程序也保持一致。

在浏览器中,推荐cookie值经过 encode 编码后保存下来,所以直接取到的也是 encode 后的值,所以追加在请求头 Cookie 字段,就不需要 decode 解码了,直接拼接即可(但基础库API的get操作最终需要进行 decode 解码)。

而对于响应头 Set-Cookie 的值,我们认为后端已经做了 encode 编码,所以前端不需要处理,直接存进 Storage 即可。

五、性能优化(高频读写)

前面实现中每次读写cookie都会调用小程序Storage API(而且是同步的),小程序框架会读写到本地Storage。 对于高频场景,可以将cookie在内存中维护一份,读写都直接走「内存层」,有更新才同步到「Storage层」。

1. 初始化

首先需要在内存中声明一个 _COOKIES (命名自行diy),建议在cookie基础库中声明,便于统一维护。

2. 读

前面初始化时已经从Storage读取一次cookies,后续getCookie就直接读内存的 _COOKIES 即可。

3. 写

写操作直接更新内存,间接更新Storage。 如果有高频写场景,可以考虑做个任务队列进行节流。

六、单元测试

微信官方在2019年5月推出了「小程序自动化 SDK」 miniprogram-automator ,经过半年多的迭代,目前已基本稳定下来。

在购物小程序场景试用了一下,cookie相关的用例很快就完成了,简直是开发者的福音:真香!!!

实际项目中,对cookie的单元测试可以分为两类:

  1. 小程序全局范围的cookie验证(比如初始化小程序后,有没有种下版本号、访问行为等关键cookie)
  2. cookie基础库API验证(比如get/set/remove等各个API是否正常工作)

以验证 setCookie() API为例:

it('API验证:setCookie()',51); font-weight: 700;">async () => {
    await miniProgram.evaluate(() => {
        wx.CookieLib.setCookie({ // 调用API
            cookie1: let { cookies } = await miniProgram.callWxMethod('getStorageSync','cookies')
    expect(cookies['cookie1'].value).toBe(12345) // 期望成功设置cookie1为12345
})
复制代码

这里为了方便测试用例调用基础库API,在小程序启动前,把Cookie基础库(CookieLib)挂到了 wx 对象上,实现方式是使用node读写文件的API去【植入代码】:

,0);">''\n wx.CookieUtil = require(\'./lib/cookie.js\');\n'')
复制代码

七、Cookie安全

Cookie安全是一个比较大的话题,这里只简单列出和小程序相关的几个点。

path、domin、HttpOnly、Secure、SameSite

小程序中已经做了一些安全措施,比如只能走HTTPS、合法域名需要管理员到微信后台进行配置、Storage只能由写入它的小程序中访问,等等。 因此 path、domin、HttpOnly、Secure、SameSite 这些字段在小程序环境下的价值没有浏览器环境大,本例中没有使用(懒..),而实际业务场景可以按自身情况决定是否要使用。

白名单机制

  1. 前端维护(大小/数量) 通常浏览器保持的Cookie数据不超过4k,部分浏览器限制同一站点最多cookie数为20个。 如果业务庞大的话,建议在Cookie基础库做一套「白名单」机制,在白名单内才可以写入,以此防止“非法写入”或“内容超大导致信息丢失”的问题。

  2. 后台维护(网关白名单) 同样的,建议从网关层面,建立一个“可信cookie”白名单,自动过滤请求中的“非法cookie”字段。

前端防篡改

小程序前端更多是防“误改”————即在操作Cookie过程中,发生了意料之外的修改。通常发生在JS“引用拷贝”特性上,比如前面提到的内存维护一个 _Cookies ,如果有一个API getAllCookies() 直接将这份内存版cookies暴露出去,对象引用容易被连带修改。所以cookie基础库需要控制暴露API的能力范围,并对取值进行“深拷贝”。

Session

Session机制将用户状态放在了服务端维护,具备更好的安全性,而且目前各种后端对于session的存储和同步都有很成熟的技术方案,有条件的业务应以Session为主做会话保持。

指纹上报

用户访问时生成设备指纹并上报(通常是登录/结算等环节),业务后台配合风控系统,遇到异常请求时下发验证环节。

八、完整小程序实现Demo

代码片段: developers.weixin.qq.com/s/x4sFASmh7…

九、小结

本文先解析了浏览器的 Cookie机制 运作原理,然后使用「数据缓存」和「网络」能力,以 公共基础库 的形式,在小程序中实现了一套 Cookie方案。希望对大家有所帮助。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


判断H5页面环境在微信中还是小程序中 用小程序提供的wx.miniProgram.getEnv可以获取环境参数 &lt;script type=&quot;text/javascript&quot; src=&quot;https://res.wx.qq.com/open/js/jweixin-1.
wx.reLaunch和wx.navigateTo,wx.navigateTo的区别 2019-03-23 11:18:05 wx.navigateTo 用于保留当前页面、跳转到应用内的某个页面,使用 wx.navigateBack可以返回到原页面。对于页面不是特别多的小程序,通常推荐使用 wx.n
微信小程序如何从数组里取值_微信小程序 传值取值的几种方法总结 小程序里常见的取值有以下几种,一个完整的项目写下来,用到的概率几乎是100%。 列表index下标取值 页面传值 form表单取值 1. 列表index下标取值 实现方式是:data-index=&quot;{{index}}&quot
H5项目接入微信授权登录,通过 UA 区分微信还是普通浏览器: let&#160;ua&#160;=&#160;navigator.userAgent.toLowerCase(); let&#160;isWeixin&#160;=&#160;ua.indexOf(&#39;micromessenge
微信小程序获取data-xx=&quot;&quot;属性的值,自定义属性设置和获取(data-) 微信小程序&lt;view class=&quot;details-btn&quot; data-taskId=&quot;111&quot; bindtap=&#39;taskdetails&#39
小程序报错:TypeError: Cannot read property ‘addEventListener‘ of undefined 解决办法 将调试基础库由2.16.0(或者当前的) -&gt; 2.14.1 解决问题
H5跳转微信小程序-成功案例(VUE)(踩坑无数) TuoMei 已于 2022-07-29 09:52:22 修改 准备工作 根据官方提供的资料需准备以下几点: 1、已认证的服务号 2、绑定JS接口安全域名 (在微信公众平台设置) 3、IP白名单 (在微信公众平台设置) 4、将小程序和H5公众号进
微信小程序 页面跳转和数据传递实例详解 这篇文章主要介绍了微信小程序 页面跳转和数据传递实例详解的相关资料,这里附有实例代码帮助到家学习理解,需要的朋友可以参考下 微信小程序 页面跳转和数据传递 1.先导 在Android中,我们Activity和Fragment都有栈的概念在里面,微信小程序页面也
情景1.拉取公司代码演示: 因为github有墙,这里我们以gitee(码云)为例作为演示 (其实就是国产github,也非常好用~) 步骤一:打开Git界面 先在一个空文件夹右击Git Bash Here,打开git界面 步骤二:输入克隆远程仓库指令 别人复制的链接在这里获取 拿到别人赋值的链接自
如何开发微信小程序? 作为一名10多年一直从事互联网平台开发的从业者,我来回答下这个问题吧。 微信小程序开发流程总体可以归纳为4个步骤, 老张带您捋一捋整个环节,小白用户可以收藏了。 好了废话不多说,开始! 一、开发前小程序需要准备的资料 我们在开发微信小程序前,需要准备下相关资料。这个资料主要是后
原生小程序开发优化方案 为了更好的制定优化方案,我们 有必要先了解下小程序的底层架构、以及与普通网页开发的差异 小程序最终渲染载体与当下一些热门的技术 Flutter、React Native等不同,依然是浏览器内核,而不是原生客户端。 而对于传统的网页来说,UI 渲染和 JS 脚本是在同一个线程中
1,不要下两倍尺寸的图片, 小程序本身自己就会对元素缩小两倍,设计图片的一杯就已经很清晰了。 2,图片压缩,(主要是压缩静态资源,ps 可以压缩,然后有一些在线压缩工具,保持600-800kb 的静态) 3,通用的代码组件化 4,是在工程量太大可以分包,分包现在最大支持20m(一般都不会去分包的)
文章浏览阅读189次。人工智能研究实验室OpenAI在2022年11月30日发布了自然语言生成模型ChatGPT,上线两个月就已经超过一亿用户,成为了人工智能界当之无愧的超级大网红。ChatGPT凭借着自身强大的拟人化及时应答能力迅速破圈,引起了各行各业的热烈讨论。简单来说ChatGPT就是可以基于用户文本输入自动生成回答的人工智能聊天机器人。那肯定会有人说这不就是Siri嘛,虽然都是交互机器人但是两者的差别可老大了。那么ChatGPT在人机交互时为什么会有这么出色的表现?它到底会不会取代搜索引擎?90%的人真的会因为ChatG
文章浏览阅读193次。8. 导航和路由管理:掌握小程序的导航方式,如使用wx.navigateTo跳转页面、使用wx.redirect重定向页面等,学会实现页面之间的跳转和传参。1. 小程序的基本概念和架构:了解小程序的定义、特点以及与传统APP的区别,掌握小程序的运行环境、组件和API等基本概念。10. 支付功能:学习小程序的支付方式,如微信支付、支付宝支付等,了解支付流程和注意事项,学会实现小程序的支付功能。9. 用户授权和登录:了解小程序的用户授权机制,如获取用户信息、调用微信API等,学会实现用户的登录和注册功能。_微信小程序开发知识点总结
文章浏览阅读4.8k次,点赞7次,收藏18次。一、准备工作1. 安装微信开发者工具,并登录微信小程序账号;2. 准备斗地主游戏的图片资源;3. 准备斗地主游戏的音效资源;二、创建小程序1. 打开微信开发者工具,点击“新建小程序”,输入小程序名称,选择小程序的项目目录,点击“创建”;2. 在小程序的项目目录中,新建文件夹“images”,将准备好的斗地主游戏的图片资源放入“images”文件夹中;3. 在小程序的项目目录中,新建文件夹“sounds”,将准备好的斗地主游戏的音效资源放入“sounds”文件夹中;三、编写代码1. 在小程_扑克牌微信小程序代码
文章浏览阅读3.9k次,点赞3次,收藏7次。一、准备工作:1. 安装微信开发者工具,创建小程序项目;2. 准备游戏角色图片;3. 准备游戏背景音乐;二、实现步骤:1. 创建游戏页面,添加游戏角色图片,添加游戏背景音乐;2. 创建游戏角色类,定义游戏角色属性,如角色名称、角色图片、角色能力等;3. 创建游戏类,定义游戏属性,如游戏人数、游戏角色、游戏规则等;4. 创建游戏控制类,定义游戏流程,如游戏开始、游戏结束、游戏角色分配等;5. 创建游戏界面,实现游戏流程,如游戏开始、游戏结束、游戏角色分配等;6. 创建游戏结果页面,显示游戏_微信小程序游戏代码
文章浏览阅读1.7k次。1. 创建小程序项目:使用微信开发者工具创建一个小程序项目,并在项目中添加一个页面,用于模拟聊天。 2. 定义数据结构:定义一个数据结构,用于存储聊天记录,包括发送者、接收者、消息内容等信息。 3. 实现聊天功能:实现聊天功能,包括发送消息、接收消息、显示消息等功能。 4. 实现界面:使用微信小程序的界面框架,实现聊天界面,包括聊天记录列表、输入框等。代码示例:// 定义数据结构var chatData = { sender: '', receiver: '', message: '' };_制作聊天对话小程序代码
文章浏览阅读2.1k次。1、创建小程序项目:使用微信开发者工具,新建一个小程序项目,输入项目名称,选择项目目录,点击“创建”按钮,即可创建小程序项目。2、添加页面:在小程序项目中,可以添加多个页面,每个页面都有自己的页面文件,比如首页、分类页、购物车页、我的页面等。3、添加组件:在小程序项目中,可以添加多个组件,比如商品列表组件、购物车组件、订单组件等,用于在页面中显示商品信息、购物车信息、订单信息等。4、添加接口:在小程序项目中,可以添加多个接口,用于获取商品信息、购物车信息、订单信息等,以便在页面中显示。5、_微信开发者工具做一个我的商城
文章浏览阅读515次。首先在配置页面index.json 添加以下代码。然后在index.jx页面配置下拉刷新。_小程序云开发上拉刷新