最近在写小程序,转载一些别人的文章,学习下经验。
关键字:微信小程序登录、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 || '获取用户登录态失败,请退出重试' }); } }); }
这里的代码逻辑非常清晰,用流程图表示是这样的:
上述流程中 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 的处理用户登录流程是这样的:
由于这里指向后台传入了 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 方法用于实现登录功能,代码流程为:
这里,实际的动作为 获取 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 的逻辑为:
从这里我们可以看到,后台处理登录请求的视图将接收的 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 中的方法来保存和读取用户信息。