• 微信小程序源码解析


    最近在写小程序,转载一些别人的文章,学习下经验。

    学以致用123 
    2018.05.09 13:46 字数 1536 阅读 475评论 0

    关键字:微信小程序登录、login、request、getuserinfo

    本文是对使用 wafer2 实现登录的分析,对使用 wafer2 框架的小程序适用,不涉及 腾讯云音视频登录功能外的其它功能。

    参考:https://github.com/tencentyun/wafer2-client-sdk

    简介

    腾讯云音视频小程序的 demo 代码使用的是腾讯云 wafer2 框架。

    wafer2 简化了用户登录方法,在 app.js 中设置 qcloud 的 LoginUrl 然后调用 qcloud.login 或者使用 login 参数设置为 true 的 qcloud.request 请求即可。

    这里先贴出经过下面分析得出的使用 qcloud 的 login 和 request 的方法。

    首先,在 app.js 中设置 qcloud 的 LoginUrl :

    qcloud.setLoginUrl(config.url + 'login');
    

    如果只需要后台实现登录请求,那么使用 qcloud.login:

    qcloud.login({
        success: function (userInfo) {
            console.log('登录成功', userInfo);
        },
        fail: function (err) {
            console.log('登录失败', err);
        }
    });
    

    setLoginUrl 中设定的 url 的后台视图需要解析 login 的 GET请求的请求头获得code、encryptedData 和 iv ,返回的响应至少需要包含 code,如果成功执行,code 需要设为 0 而且还需要返回 data ,data 中至少需要包含 skey 、userinfo 和 iminfo 三项内容。

    如果后台请求之前需要自动登录,那么使用 qcloud.request :

    qcloud.setLoginUrl(myloginUrl);
    qcloud.request({
        login: true,
        url: myUrl,
        success: function (response) {
            console.log(response);
        },
        fail: function (err) {
            console.log(err);
        }
    });
    

    注意,qcloud.request 通过构造请求头向后台传送数据。

    下面的内容是具体代码分析。

    现有代码分析

    demo 代码并没有使用 wafer2 内置的登录函数,而是在与 config.js 同一个目录下的 getloginInfo.js 中通过 wx.login 和 wx.getUserInfo 实现登录的。这里的实现代码为:

    // 获取微信登录信息,用于获取openid
    function getLoginInfo(options) {
      wx.login({
        success: function (res) {
          if (res.code) {
            console.log('获取code成功',res.code);
            options.code = res.code;
            // 获取用户信息
            wx.getUserInfo({
              withCredentials: false,
              success: function (ret) {
                options.userName = ret.userInfo.nickName;
                proto_getLoginInfo(options);
              },
              fail: function() {
                proto_getLoginInfo(options);
              }
            });
          } else {
            console.log('获取用户登录态失败!' + res.errMsg);
            options.fail && options.fail({
              errCode: -1,
              errMsg: '获取用户登录态失败,请退出重试'
            });
          }
        },
        fail: function (res) {
          console.log('获取用户登录态失败!' + res.errMsg);
          if (ret.errMsg == 'request:fail timeout') {
            var errCode = -1;
            var errMsg = '网络请求超时,请检查网络状态';
          }
          options.fail && options.fail({
            errCode: errCode || -1,
            errMsg: errMsg || '获取用户登录态失败,请退出重试'
          });
        }
      });
    }
    

    这里的代码逻辑非常清晰,用流程图表示是这样的:


     
    getlogininfo_wxlogin.jpg

    上述流程中 wx.login 及 wx.getUserInfo 的详细信息请查看官网文档。

    代码执行成功会调用 proto_getLoginInfo 函数,如果失败,则运行调用输入参数的失败处理函数。

    proto_getLoginInfo 在 getLoginInfo 的下面,主要实现腾讯云的 RoomService 登录功能,但是由于 RoomService 登录需要传入后台计算得到的用户签名,这里使用了 wx.request 向后台发送 GET 请求,发送请求的具体代码为:

    function proto_getLoginInfo(options) {
      wx.request({
        url: config.serverUrl + '/weapp/utils/get_login_info',
        data: { userIDPrefix: 'weixin', code: options.code },
        method: 'GET',
        header: {
          'content-type': 'application/json' // 默认值
        },
        success: function (ret) {
          if (ret.data.code) {
            console.log('获取登录信息失败,调试期间请点击右上角三个点按钮,选择打开调试');
            options.fail && options.fail({
              errCode: ret.data.code,
              errMsg: ret.data.message + '[' + ret.data.code + ']'
            });
            return;
          }
          console.log('获取IM登录信息成功: ', ret.data);
          ...
        },
        fail: function (ret) {
          console.log('获取IM登录信息失败: ', ret);
          if (ret.errMsg == 'request:fail timeout') {
            var errCode = -1;
            var errMsg = '网络请求超时,请检查网络状态';
          }
          options.fail && options.fail({
            errCode: errCode || -1,
            errMsg: errMsg || '获取登录信息失败,调试期间请点击右上角三个点按钮,选择打开调试'
          });
        }
      });
    }
    

    这里的 wx.request 向后台发送 code 参数,后台使用 code 换取 openid 然后计算用户签名,代码中的登录功能结束。

    然而, wafer2 的处理用户登录流程是这样的:

     
    wxlogin.png

    由于这里指向后台传入了 code ,所以后台只能实现红色框中的内容。前台也没有实现紫色框中的保存会话功能。

    我们可以将 wx.getUserInfo 的 withCredentials 设置为 true ,然后为 wx.request 传入 encryptedData 和 iv ,完成上图中后台的工作,并使用 wx.setStorageSync(KEY,DATA) 保存会话。

    然而,我们完全可以使用 wafer2 内置函数实现登录。

    使用 wafer2 内置函数实现登录

    查看 app.js 文件,我们可以看到:

    var qcloud = require('./lib/index');
    
    App({
      onLaunch: function () {
        // 展示本地存储能力
        // qcloud.setLoginUrl(config.url + 'getwxinfo');
        // qcloud.setLoginUrl(config.url + 'login');
      },
      globalData: {
        userInfo: null
      }
    })
    

    这里注释掉了 qcloud.setLoginUrl 。打开定义为 qcloud 的 ./lib/index 文件,代码包括:

    var constants = require('./constants');
    var login = require('./login');
    var Session = require('./session');
    var request = require('./request');
    var Tunnel = require('./tunnel');
    var SocketTunnel = require('./socketTunnel');
    
    var exports = module.exports = {
        login: login.login,
        setLoginUrl: login.setLoginUrl,
        LoginError: login.LoginError,
    
        clearSession: Session.clear,
    
        request: request.request,
        RequestError: request.RequestError,
    
        Tunnel: Tunnel,
        SocketTunnel: SocketTunnel,
    };
    
    // 导出错误类型码
    Object.keys(constants).forEach(function (key) {
        if (key.indexOf('ERR_') === 0) {
            exports[key] = constants[key];
        }
    });
    

    从上面的代码中,我们可以看到,除了 setLoginUrl ,qcloud 还有 login、clearSession、request、Tunnel、SocketTunnel、LoginError、RequestError 等方法。

    login 函数需要在 app.js 中使用 setLoginUrl 设置与后台交互的 url 。设置 url 后,我们需要了解 login 是如何工作的,从而确认后台如何与 login 交互。

    login.js 分析

    打开 wxlite/lib/login.js 文件,我们可以看到模块输出三个方法:

    module.exports = {
        LoginError: LoginError,
        login: login,
        setLoginUrl: setLoginUrl,
    };
    

    setLoginUrl

    我们先看 setLoginUrl ,这个函数的功能非常简单,设置后台登录 url :

    var setLoginUrl = function (loginUrl) {
        defaultOptions.loginUrl = loginUrl;
    };
    

    代码中,只是将 defaultOptions 的 loginUrl 设置为输入的 loginUrl 。这里 defaultOptions 是 login.js 定义的全局变量,它的初始化设置为:

    var defaultOptions = {
        method: 'GET',
        success: noop,
        fail: noop,
        loginUrl: null,
    };
    

    这里的 noop 为:

    var noop = function noop() {};
    

    LoginError

    LoginError 是处理登录错误的,其代码为:

    var LoginError = (function () {
        function LoginError(type, message) {
            Error.call(this, message);
            this.type = type;
            this.message = message;
        }
    
        LoginError.prototype = new Error();
        LoginError.prototype.constructor = LoginError;
    
        return LoginError;
    })();
    

    login

    流程

    login 方法用于实现登录功能,代码流程为:

     
    login流程.jpg

    这里,实际的动作为 获取 session,检查 session并进行相应操作。

    获取 session 使用的 Session.get() 方法,打开 Session 对应的 session.js 文件,get() 方法的功能为从微信缓存中获取 'weapp_session_F2C224D4-2BCE-4C64-AF9F-A6D872000D1A' 对应的值。这个值是在第一运行 doLogin 时设置的。那么,我们接下来分析 doLogin 。

    doLogin

    doLogin 的代码为:

    var doLogin = () => getWxLoginResult(function (wxLoginError, wxLoginResult) {
        if (wxLoginError) {
            options.fail(wxLoginError);
            return;
        }
        
        var userInfo = wxLoginResult.userInfo;
    
        // 构造请求头,包含 code、encryptedData 和 iv
        var code = wxLoginResult.code;
        var encryptedData = wxLoginResult.encryptedData;
        var iv = wxLoginResult.iv;
        var header = {};
    
        header[constants.WX_HEADER_CODE] = code;
        header[constants.WX_HEADER_ENCRYPTED_DATA] = encryptedData;
        header[constants.WX_HEADER_IV] = iv;
    
        // 请求服务器登录地址,获得会话信息
        wx.request({
            url: options.loginUrl,
            header: header,
            method: options.method,
            data: options.data,
            success: function (result) {
                var data = result.data;
    
                // 成功地响应会话信息
                if (data && data.code === 0 && data.data.skey) {
                    var res = data.data
                    if (res.userinfo) {
                        Session.set(res.skey);
                        options.success(res.iminfo);
                    } else {
                        var errorMessage = '登录失败(' + data.error + '):' + (data.message || '未知错误');
                        var noSessionError = new LoginError(constants.ERR_LOGIN_SESSION_NOT_RECEIVED, errorMessage);
                        options.fail(noSessionError);
                    }
    
                // 没有正确响应会话信息
                } else {
                    var noSessionError = new LoginError(constants.ERR_LOGIN_SESSION_NOT_RECEIVED, JSON.stringify(data));
                    options.fail(noSessionError);
                }
            },
    
            // 响应错误
            fail: function (loginResponseError) {
                var error = new LoginError(constants.ERR_LOGIN_FAILED, '登录失败,可能是网络错误或者服务器发生异常');
                options.fail(error);
            },
        });
    });
    

    dologin 实行的是 getWxLoginResult 方法,这里定义的是方法成功或失败的回调方法。getWxLoginResult 的定义为:

    var getWxLoginResult = function getLoginCode(callback) {
        wx.login({
            success: function (loginResult) {
                wx.getUserInfo({
                    success: function (userResult) {
                      console.log('[userResult]', userResult);
                        callback(null, {
                            code: loginResult.code,
                            encryptedData: userResult.encryptedData,
                            iv: userResult.iv,
                            userInfo: userResult.userInfo,
                        });
                    },
    
                    fail: function (userError) {
                      console.log('[userError]', userError);
                        var error = new LoginError(constants.ERR_WX_GET_USER_INFO, '获取微信用户信息失败,请检查网络状态');
                        error.detail = userError;
                        callback(error, null);
                    },
                });
            },
    
            fail: function (loginError) {
              console.log('[loginError]', loginError);
                var error = new LoginError(constants.ERR_WX_LOGIN_FAILED, '微信登录失败,请检查网络状态');
                error.detail = loginError;
                callback(error, null);
            },
        });
    };
    

    结合这两个方法,可以分析 doLogin 的逻辑为:

     
    doLogin流程.jpg

    从这里我们可以看到,后台处理登录请求的视图将接收的 code、encryptedData 和 iv 放在请求头中,后台返回的响应至少需要返回 code、data 两个元素,如果成功执行,code 需要设为 0 ,data 中至少需要包含 skey 、userinfo 和 iminfo 三项内容。

    用法

    我们已经了解了 login 的基本原理,那么,我们应该怎样使用 login 呢?

    与 login.js 位于同一文件夹的 request.js 提供设置 login 为 ture 的 Request 请求实现先登录后请求,我们看下这个方法是怎么使用 login.js 的。

    request 中,如果定义了 login 为 true ,则requirLogin 为 true,请求将执行doRequestWithLogin()。

    if (requireLogin) {
        doRequestWithLogin();
    } else {
        doRequest();
    }
    

    doRequestWithLogin() 定义了登录后再请求的函数:

    function doRequestWithLogin() {
        loginLib.login({ success: doRequest, fail: callFail });
    }
    

    如果登录成功,将执行 doRequest() ,失败则执行 callFail()。

    doRequest 执行时首先构造 authHeader 将从微信缓存中获取的 'weapp_session_F2C224D4-2BCE-4C64-AF9F-A6D872000D1A' 的值写到 X-WX-Skey 请求头中进行发送。

    问题

    整个流程中,如果登录过之后再次调用 login ,返回 Session.get() 中的 userinfo 。但是在首次登录成功时或者 request 设置login 为 true 的 request 成功时,Session.set() 设置的是 skey 的值,并没有 userInfo 的值。是否可以扩展 Session 中的方法来保存和读取用户信息。

  • 相关阅读:
    python设计模式之命令模式
    [Shell] 生成日期列表
    [Python] Python 获取文件路径
    [Search] 倒排索引与bool检索
    [NLP] Reformer: The Efficient Transformer
    [Alg] 随机抽样完备算法-蓄水池算法 Reservoir Sampling
    [Chaos] 混沌数学学习资料
    [Alg] 文本匹配-多模匹配-WM算法
    [Alg]文本匹配-单模匹配-Sunday算法
    [Alg] 文本匹配-多模匹配-AC自动机
  • 原文地址:https://www.cnblogs.com/wang-sai-sai/p/10729376.html
Copyright © 2020-2023  润新知