小程序简单开发(一)-关于登陆的一些事

小程序简单开发(一)-关于登陆的一些事

梳理登陆流程

这里有几个点是要注意的:html

  1. 要注意 es6语法使用,es6的语法会在小程序里面更加的有用,其中最关键的地方就是小程序的 api 大部分都是异步的,旧有的方式要异步就必需要要回调,而回调的就会致使代码逻辑容易发生混乱,因此须要使用promise而且进行接口的封装。
  2. 要注意效率,例如对于 code 的获取,首次和非首次要注意首次获取后保存下来,而后非首次获取就能够从缓存里面直接获取,相似这种有好几个地方,因此须要注意好。
  3. 要注意先理清楚业务逻辑和登陆逻辑和产品体验逻辑,必定要先画好流程图,由于小程序的逻辑相较通常的逻辑要稍微“绕”,对,是很绕的绕,因此须要注意理顺流畅。
  4. 要注意接口的调用前后顺序,跟流程图的关系很大,对比官方文档,一步步处理,又由于使用异步,因此须要弄清楚 promise 异步的 resolve 和 reject,在多重promise 里面会比较麻烦,不过也是有技巧能够回避的,详情下面会说。
  5. 须要注意签名解密处理,虽然这是服务器端作的,可是也须要了解好是怎么一个操做,由于也须要解析给服务器端的同事,如何配合小程序来作处理。
  6. 须要理解各个关键的变量元素的意思,code,session_key,3rd_session,openid,unioinid的意义,这样才能协助理解文档的总体思路。
  7. 须要注意 openId 和 unioinId的使用

微信小程序登陆流程图

引用Yinjie 的图,由于这个图比官方的要看得明白一点。git

clipboard.png

代码逻辑流程图

引用Yinjie 的图,由于这个图比官方的要看得明白一点。es6

clipboard.png

腾讯 weapp-session的代码流程图

引用腾讯 weapp-session 的图github

clipboard.png

他们还出了一个比较详细的,一步步的代码处理流程,能够对比本身的程序进行检查。
  1. 客户端(微信小程序)发起请求 request
  2. weapp-session-client 包装 requestredis

    • 首次请求算法

      • 调用 wx.login()wx.getUserInfo() 接口得到 coderawDatasignature
      • requset 的头部带上 coderawDatasignature
      • 保存 code 供下次调用
    • 非首次请求数据库

      • request 的头部带上保存的 code
  3. 服务器收到请求 request,中间件从头部提取 coderawDatasignature 字段小程序

    • 若是 code 为空,跳到第 4
    • 若是 code 不为空,且 rawData 不为空,须要进行签名校验微信小程序

      • 使用 codeappidapp_secret 请求微信接口得到 session_keyopenidapi

        • 若是接口失败,响应 ERR_SESSION_KEY_EXCHANGE_FAILED
      • 使用签名算法经过 rawDatasession_key 计算签名 signature2
      • 对比 signaturesignature2

        • 签名一致,解析 rawDatawxUserInfo

          • openid 写入到 wxUserInfo
          • (code, wxUserInfo) 缓存到 Redis
          • wxUserInfo 存放在 request.$wxUserInfo
          • 跳到第 4
        • 签名不一致,响应 ERR_UNTRUSTED_RAW_DATA
    • 若是 code 不为空,但 rawData 为空,从 Redis 根据 code 查询缓存的用户信息

      • 找到用户信息,存放在 request.$wxUserInfo 字段里,跳到第 4
      • 没找到用户信息(多是过时),响应 ERR_SESSION_EXPIRED
  4. request 被业务处理,可使用 request.$wxUserInfo 来获取用户信息(request.$wxUserInfo 可能为空,业务须要自行处理)

流程图总结

  1. code 是微信用户的登陆凭证,打开小程序登陆的时候获取的只属于微信这个用户的登陆凭证,须要注意的是,这个登陆凭证只供微信小程序使用的。
  2. session_key 是微信用户在小程序里面的登陆态信息,至关因而微信给这个用户颁发的一个登陆 session,官网地址

    • 他有一个过时时间{"session_key":"...","expires_in":7200,"openid":"..."},须要按期使用wx.checkSession检测。
  3. openId ,用户的惟一标识
  4. unioinId,若是开发者拥有多个移动应用、网站应用、和公众账号(包括小程序),可经过unionid来区分用户的惟一性,由于只要是同一个微信开放平台账号下的移动应用、网站应用和公众账号(包括小程序),用户的unionid是惟一的。换句话说,同一用户,对同一个微信开放平台下的不一样应用,unionid是相同的。
  5. 通常来讲,openId 就是微信用户的惟一标识,可是由于微信产品不少,因此会出现多个微信产品使用不一样的 openId 来标识用户,可是对于咱们作业务接入的话,就买办法使用了,因此建议是统一使用 unioinid,由于通常来讲,通常的业务都会有公众号,小程序联合使用的。
  6. 3rd_session 是通常是指咱们本身公司的服务器的 session,通常来讲,能够跟原来的业务的 session 一块儿使用,不过这个 session 的过时时间必定要比小程序的session_key 的过时时间要长,这样能够减小 session 的屡次重复建立,另一般咱们本身公司的服务器的 session 管理都会使用相似 redis 之类的数据库进行管理的,这个大体了解一下,由于其余文章会提到。
  7. 为何要用2个 sessionsession_key3rd_session),那是由于session_key是微信的登陆态,3rd_session是咱们业务系统的登陆态,两边各有一个登陆态,因此须要将2个登陆态合并为一个 session,在这里面是合并为3rd_session,并保存到咱们业务系统上,而后每次须要使用的时候,小程序带上这个3rd_session访问咱们的业务系统,经过处理,能够返回咱们须要 unioinid 和其余 session 信息而不用每次都去获取一个新的session_key(由于微信有限制code 的使用,要用 code 换 session_key),总的来讲,就是使用3rd_session来管理小程序的登陆态

关于解密

根据官方文档: ,数据校验是为了提升数据的安全性,咱们须要获取用户的 unioinid,须要调用wx.getUserInfo接口来获取,可是通常状况下,获取出来的数据是

{
  "nickName": "Band",
  "gender": 1,
  "language": "zh_CN",
  "city": "Guangzhou",
  "province": "Guangdong",
  "country": "CN",
  "avatarUrl": "http://wx.qlogo.cn/mmopen/vi_32/1vZvI39NWFQ9XM4LtQpFrQJ1xlgZxx3w7bQxKARol6503Iuswjjn6nIGBiaycAjAtpujxyzYsrztuuICqIM5ibXQ/0"
}

对于须要业务接入的话,没有 unioinid 是没意义的,因此须要根据官方的方式来进行解密,解密后的数据里面有 unioinid 了

{
    "openId": "OPENID",
    "nickName": "NICKNAME",
    "gender": GENDER,
    "city": "CITY",
    "province": "PROVINCE",
    "country": "COUNTRY",
    "avatarUrl": "AVATARURL",
    "unionId": "UNIONID",
    "watermark":
    {
        "appid":"APPID",
    "timestamp":TIMESTAMP
    }
}

封装网络接口

由于小程序的全部网络请求都是异步的,那么异步就会出现不少重的回调的问题,因此改为了 promise,promise 的使用要谨慎注意 resolve 和 return的处理。

例如这样:

const httpRequest = (api, data) => {
    let serverHost = env.serverHost;

    return new Promise(function (resolve, reject) {
        //发起网络请求
        wx.request({
            url: serverHost + api,
            data: data,
            header: {
                'content-type': 'application/x-www-form-urlencoded'
            },
            success: function (res) {
                if (res.data.errno === 0) {
                    // 须要下一步处理的就用 resolve 返回
                    resolve(res.data);
                }
                else {
                    console.log("http fail:api:" + api + "res:" + JSON.stringify(res));
                    // 须要跳出循环处理的就用 reject
                    reject(res.data);
                }
            }, fail: function (res) {
                console.log("http fail:api:" + api + "res:" + JSON.stringify(res));
                // 须要跳出循环处理的就用 reject
                reject(res);
            }
        })
    })
};

在多重 promise 的状况下,则须要注意2个地方:

  1. 须要返回一个可以使用的 promise 实例
  2. 即便有异步而且出现分支的状况下,尽可能集中在一个统一的流里面处理,用统一的 reject 跳出,而不是各个异步单独跳出,这样流程会更加统一和方便管理。
getCode() // 获取 wx code
            .then(code => {
                wxCode = code;
                // 这里getSetting是一个返回的 promise实例,如上面的那个
                return getSetting(); // 获取 setting
            })
            .then(res => {
                if (res.authSetting["scope.userInfo"]) {
                    // 这里getUserInfo是一个返回的 promise实例
                    return getUserInfo(self);
                } else {
                    console.log("first auth none:" + JSON.stringify(res));
                    // 相似
                    return util.showModal("亲,尚未受权!")
                        .then(res => {
                            return getAuth("userInfo");
                        }).then(res => {
                            // 检查受权是否正常
                            return getSetting();
                        })
                        .then(res => {
                            if (res.authSetting["scope.userInfo"]) {
                                return getUserInfo(self);
                            } else {
                                return Promise.reject(res);
                            }
                        });
                }
            })
            .catch(err=>{
                console.log("err"+err);    
            })

注意友好的提示

微信小程序“受权失败”场景须要优雅处理,提高用户体验,参考这里:能够稍微看到是如何生效的:

经过在wx.getSetting里面插入一个判断处理,判断没有权限即弹出 modal 提示框:

wx.getSetting({
    success: function success(res) {
        console.log(res.authSetting);
        var authSetting = res.authSetting;
        if (authSetting['scope.userInfo'] === false) {
                wx.showModal({
                    title: '用户未受权',
                    content: '如需XXXXXXX。',
                    showCancel: false,
                    success: function (res) {
                        if (res.confirm) {
                            console.log('用户点击肯定')
                            wx.openSetting({
                                success: function success(res) {
                                }
                            });
                        }
                    }
                })
        }
    }
});

参考引用:

  • 鸣谢里面提到的全部引用来源的做者。
相关文章
相关标签/搜索