国产亚洲精品资源在线26u

      
      
      <th id="ljgpb"><pre id="ljgpb"><sup id="ljgpb"></sup></pre></th>

      <rp id="ljgpb"></rp>

      您現在的位置: 微信小程序 > 微信小程序開發 > 教程 >

      京東購物小程序cookie方案實踐

      來源:微信小程序 編輯:Yiyongtong.com 發布時間:2020-05-22 10:18熱度:

      早期為了解決“會話保持”的需求,社區中出現了「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)
      // ?。?/span>
      wx.getStorageSync('cookies')
      復制代碼

      其中 cookies 的「存儲結構」如下:

      // cookies = 
      {
          cookie1: { // “最小cookie單元” ==> cookieItem
              name: 'cookie1', // cookie名
              value: 'xxx',    // 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'
      })
      
      setCookie({
          cookie1: {
              value: 12345,
              maxAge: 3600 * 24  // 自定義有效期(這里示例是24小時)
          },
          cookie2: {
              value: '12345',
              expires: 'Wed, 21 Oct 2015 07:28:00 GMT' // 標準GMT格式
          }
      })
      復制代碼

      這里可對入參遍歷,而cookie子項無論直接傳值value還是傳了詳細object,都盡量的獲取 name/value/expires/maxAge ,傳給格式化函數轉為標準的 cookieItem :

      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———— removeCookie()

      步驟:從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
      

      以下是對一個請求的抓包示例:

      在小程序中,請求發起有兩種方式: HTTP 和 WebSocket ,這里以HTTP為例,先對請求api進行「封裝」:

      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格式 ==> 設置到 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 (服務端)中是這樣描述:

      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值經過 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()', 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)掛到了 wx 對象上,實現方式是使用node讀寫文件的API去【植入代碼】:

      fs.appendFileSync('./your_project/app.js', ''\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方案。希望對大家有所幫助。


      国产亚洲精品资源在线26u

          
          
          <th id="ljgpb"><pre id="ljgpb"><sup id="ljgpb"></sup></pre></th>

          <rp id="ljgpb"></rp>