微信網頁登陸邏輯與實現

如今的網站開發,都繞不開微信登陸(畢竟微信已經成爲國民工具)。雖然文檔已經寫得很詳細,可是對於沒有經驗的開發者仍是容易踩坑。javascript

因此,專門記錄一下微信網頁認證的交互邏輯,也方便本身往後回查:css

  1. 加載微信網頁sdk
  2. 繪製登錄二維碼:新tab頁面繪製 / 本頁面iframe繪製
  3. 用戶掃碼登錄,前端跳入回調網址
  4. 回調網址進一步作邏輯處理,若是是頁內iframe繪製二維碼,須要通知頂級頁

🔍閱讀更多系列文章 / 閱讀原文🔍html

微信網頁SDK加載

在多人團隊協做中,加載資源的代碼須要格外當心。由於可能會有多個開發者在同一業務邏輯下調用,這會形成資源的重複加載。前端

處理方法有兩種,第一種是對外暴露多餘接口,專門check是否重複加載。可是考慮到調用者每次在加載前,都須要顯式調用check()方法進行檢查,不免會有遺漏。java

因此採用第二種方法--設計模式中的緩存模式,代碼以下:webpack

// 備忘錄模式: 防止重複加載
export const loadWeChatJs = (() => {
  let exists = false; // 打點
  const src = '//res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js'; // 微信sdk網址

  return () => new Promise((resolve, reject) => {
    // 防止重複加載
    if(exists) return resolve(window.WxLogin);
    
    let script = document.createElement('script');
    script.src = src;
    script.type = 'text/javascript';
    script.onerror = reject; // TODO: 失敗時候, 能夠移除script標籤
    script.onload = () => {
      exists = true;
      resolve(window.WxLogin);
    };
    document.body.appendChild(script);
  });
})();

繪製登錄二維碼

根據《微信登錄開發指南》,將參數傳遞給window.WxLogin()便可。css3

// 微信默認配置
const baseOption = {
  self_redirect: true, // true: 頁內iframe跳轉; false: 新標籤頁打開 
  id: 'wechat-container', 
  appid: 'wechat-appid',
  scope: 'snsapi_login',
  redirect_uri: encodeURIComponent('//1.1.1.1/'),
  state: '',
};

export const loadQRCode = (option, intl = false, width, height) => {
  const _option = {...baseOption, ...option};

  return new Promise((resolve, reject) => {
    try {
      window.WxLogin(_option);
      const ele = document.getElementById(_option['id']);
      const iframe = ele.querySelector('iframe');
      iframe.width = width? width : '300';
      iframe.height = height? height : '420'; 
      // 處理國際化
      intl && (iframe.src = iframe.src + '&lang=en');
      resolve(true);
    } catch(error) {
      reject(error);
    }
  });
};

在須要使用的業務組件中,能夠在周期函數componentDidMount調用,下面是demo代碼:git

componentDidMount() {
    const wxOption = {
        // ...
    };
    loadWeChatJs()
        .then(WxLogin => loadQRCode(wxOption))
        .catch(error => console.log(`Error: ${error.message}`));   
}

回調網址與iframe通訊

這一塊我以爲是微信登錄交互中最複雜和難以理解的一段邏輯。開頭有講過,微信二維碼渲染有2中方式,一種是打開新的標籤頁,另外一種是在指定id的容器中插入iframe。es6

毫無疑問,第二種交互方式更友好,由於要涉及不一樣級層的頁面通訊,代碼處理也更具挑戰。github

爲了方便說明,請先看模擬的數據配置:

// redirect 地址會被後端拿到, 後端重定向到此地址, 前端會訪問此頁面
// redirect 地址中的參數, 是前端人員留給本身使用的; 後端會根據業務須要, 添加更多的字段, 而後一塊兒返回前端
const querystr = '?' + stringify({
  redirect: encodeURIComponent(`${window.location.origin}/account/redirect?` + stringify({
    to: encodeURIComponent(window.location.origin),
    origin: encodeURIComponent(window.location.origin),
    state: 'login'
  })),
  type: 'login'
});

const wxOption = {
  id: 'wechat-container',
  self_redirect: true,
  redirect_uri: encodeURIComponent(`//1.1.1.1/api/socials/weixin/authorizations${querystr}`) // 微信回調請求地址
};

先後端、微信服務器、用戶端交互邏輯

按照上面的配置,我描述一下前端、用戶端、微信服務器和後端交互的邏輯:

  1. 前端根據wxOption加載了二維碼,全部信息都放在了二維碼中。同時監聽微信服務器的消息。
  2. 用戶手機掃碼,通知微信服務器肯定登錄。
  3. 微信服務器接受到用戶的掃碼請求,轉發給前端。
  4. 前端收到微信服務器傳來消息,根據wxOption的redirect_uri參數,跳轉到此url地址。注意:

    • 這個接口地址是後端的,請求方式是GET
    • 前端經過拼接params攜帶參數
    • 地址會被拼接微信服務器傳來的一個臨時token,用於交給後端換取用戶公衆密鑰
  5. 後端接收到/api/socials/weixin/authorizations${querystr}的請求,decode解碼querystr中的信息。而後向微信服務端請求用戶公衆密鑰。根絕先後端的約定(demo中用的是redirect字段),重定向到前端指定的redirect字段,而且拼接用戶公衆密鑰等更多信息。
  6. 前端知悉重定向,跳到重定向的路由(demo中用的是/account/redirect)
  7. 在對應的路由處理後端傳來的用戶密鑰等數據便可
  8. 至此,微信認證的四端交互邏輯完成

跨Iframe通訊

前面流程走完了,如今的狀況是頁面中iframe的二維碼區域,已經被替換成了/account/redirect?...的內容。

爲了實現通訊,須要在頁面的週期中監聽message事件,並在組件卸載時,卸載此事件:

componentDidMount() {
  // ... ...
  
  window.addEventListener('message', this.msgReceive, false);
}

componentWillUnmount() {
  window.removeEventListener('message', this.msgReceive);
}

msgReceive(event) {
  // 監測是不是安全iframe
  if(!event.isTrusted) {
    return;
  }
  console.log(event.data); // 獲取iframe中傳來的數據, 進一步進行邏輯處理
}

而在/account/redirect?...路由對應的組件中,咱們須要解析路由中的params參數,按照業務邏輯檢查後,將結果傳遞給前面的頁面:

componentDidMount() {
    // step1: 獲取url中params參數
    const querys = getQueryVariable(this.props.location.search);
    // step2: 檢查querys中的數據是否符合要求 ... 
    // step3: 向頂級頁面傳遞消息
    return window.parent && window.parent.postMessage('data', '*');
}

至此,微信網頁認證的流程完成。

更多:關於iframe通訊的更多細節,請查看MDN的文檔

更多系列文章

⭐在GitHub上收藏/訂閱⭐

《前端知識體系》

《設計模式手冊》

《Webpack4漸進式教程》

⭐在GitHub上收藏/訂閱⭐

相關文章
相關標籤/搜索