前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >微信网页登录逻辑与实现

微信网页登录逻辑与实现

作者头像
心谭博客
发布2020-04-20 16:58:22
3.7K0
发布2020-04-20 16:58:22
举报
文章被收录于专栏:YuanXinYuanXinYuanXin

现在的网站开发,都绕不开微信登录(毕竟微信已经成为国民工具)。虽然文档已经写得很详细,但是对于没有经验的开发者还是容易踩坑。

所以,专门记录一下微信网页认证的交互逻辑,也方便自己日后回查:

  1. 加载微信网页 sdk
  2. 绘制登陆二维码:新 tab 页面绘制 / 本页面 iframe 绘制
  3. 用户扫码登陆,前端跳入回调网址
  4. 回调网址进一步做逻辑处理,如果是页内 iframe 绘制二维码,需要通知顶级页

微信网页 SDK 加载

在多人团队协作中,加载资源的代码需要格外小心。因为可能会有多个开发者在同一业务逻辑下调用,这会造成资源的重复加载。

处理方法有两种,第一种是对外暴露多余接口,专门 check 是否重复加载。但是考虑到调用者每次在加载前,都需要显式调用check()方法进行检查,难免会有遗漏。

所以采用第二种方法–设计模式中的缓存模式,代码如下:

// 备忘录模式: 防止重复加载
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()即可。

// 微信默认配置
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 代码:

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

回调网址与 iframe 通信

这一块我觉得是微信登陆交互中最复杂和难以理解的一段逻辑。开头有讲过,微信二维码渲染有 2 中方式,一种是打开新的标签页,另一种是在指定 id 的容器中插入 iframe。

毫无疑问,第二种交互方式更友好,因为要涉及不同级层的页面通信,代码处理也更具挑战。

为了方便说明,请先看模拟的数据配置:

// 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,用于交给后端换取用户公众密钥
  1. 后端接收到/api/socials/weixin/authorizations${querystr}的请求,decode 解码 querystr 中的信息。然后向微信服务端请求用户公众密钥。根绝前后端的约定(demo 中用的是 redirect 字段),重定向到前端指定的 redirect 字段,并且拼接用户公众密钥等更多信息。
  2. 前端知悉重定向,跳到重定向的路由(demo 中用的是/account/redirect)
  3. 在对应的路由处理后端传来的用户密钥等数据即可
  4. 至此,微信认证的四端交互逻辑完成

跨 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 的文档

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-04-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 微信网页 SDK 加载
  • 绘制登陆二维码
  • 回调网址与 iframe 通信
  • 前后端、微信服务器、用户端交互逻辑
  • 跨 Iframe 通信
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档