小程序如何安全的登录

近期刚好开发到有关小程序登录相关的逻辑,正好和大家好好聊一聊小程序登录逻辑。小程序登录的目的是为了更安全的账号体系,由于和微信支付绑定的很紧密。所以这里的登录更要要求严谨。之前看到过临时tid(有效期),或保存openid等方式,感觉多存在被人抓包复制,攻击的可能性。我通过仔细阅读官方文档,整理了一个,仅依靠一次性生成code的方式登录,可以保证登录用的“凭据”仅可使用一次,避免盗刷。

登录是什么?

抛开网上很多官方的解释,我认为就是让用户的客户端,可以在服务端指定一个固定的数据库Key。即用户携带登录“凭据”,服务端通过这个“凭据”转化成后台数据库中唯一的key(找到这个用户的所有信息),并可以凭借这个Key完成对用户信息的操作。而具体用什么方式,其实万变不离其宗。即如下图:

                      

携带登录凭证是什么?有什么要求?

每次登录携带的一个密钥(一下称为code),这个密钥就是打开用户数据库的钥匙,是用户身份的必要条件。那么我们对这个key有什么要求呢?

  • 可用于后端校验:说白了就是后端的用户ID不能明文存在前端(小程序或请求参数中),我们提供的code,不是用户ID,但是它必须可以(在服务端)转化成用户ID。
  • 可变性:如果code长期不变,那别人就相当于掌握了code到用户ID的映射关系,如果code长期不变,其实和直接传用户ID没什么区别,也就是没有安全性可言。顺带提一下有一些app采用的是一段有效期内不变的“有效期code”,不是说不好,对于不涉及现金支付的业务,也算是够用。如果涉及比较严谨的支付逻辑,最好还是采用“一次性密钥code”,这样别人就很难抓包盗用你的账户。
  • 随机性:即使是可变code,也不能有规律可循,也就是说生成code的方法要够随机

搞一个“登录凭证”吧

这样来看,我们的第一个目标是搞到这个code,其实小程序官方提供了这样一个API: 

wx.login(Object object)
调用接口获取登录凭证(code)。通过凭证进而换取用户登录态信息,包括用户的唯一标识(openid)及本次登录的会话密钥(session_key)等。用户数据的加解密通讯需要依赖会话密钥完成。更多使用方法详见 小程序登录。

注意:这个API并不是调用后就直接登录的,通过前面的我的解释,大家可以理解,这个其实就是那个生成“用户登录凭证”code,这个“登录凭证有效期5分钟”而且只能用一次。我们需要在http请求中携带这个code,以方便服务端登录验证并获取用户信息。更重要的是你需要每次发起http请求都重新生成一个(可以很好的防止“重放攻击”),因此我建议把获取code的代码放在http请求的组件里,对需要登录态的请求添加code参数。

                          

这样前端部分就算是准备好了,下面我们来看服务端需要做什么。

服务端怎么使用(验证)code?

code通过参数的形式到达服务端后,我们的验证方式也是由微信官方提供,即调用微信的“auth.code2Session”接口,接口文档:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html

             

其中,appid是注册小程序的时候获取的,secret也是注册小程序的时候获取的,不要轻易改,查看后赶紧记好,js_code就是上面我们说的“登录凭证code”,grant_type就填“authorization_code”。然后在server端把这个请求发出去

var code = req.query.code || req.body.code || req.headers.code;
  var url = `https://api.weixin.qq.com/sns/jscode2session?appid=${
    wechatConfig.appId
  }&secret=${
    wechatConfig.secret
  }&js_code=${code}&grant_type=authorization_code`;
  
  request.get(url, function(error, response, body) {
    if (!error) {
      if (body && body.length > 0) {
        var parseObject = JSON.parse(body);
        if (!req.session) {
          req.session = {};
        }
        // openid :  parseObject.openid;
        // unionId:  parseObject.unionid;
        // todo: About openid & unionid
        next();
      }
    } else {
      console.log('err:', error);
      next();
    }

 服务端甚至可以用这个unionId和openid直接(或稍加改造)入库做用户信息的key。剩下就是各自业务逻辑的事了。

openId和unionId都是什么?

微信服务器返回两个用户唯一ID,这两个id都可以标识唯一用户,也都可以作为我们业务的账号体系,有效期为永远不变(迁移接口的情况不算),但是他们俩是有区别的

  • openId是用户在当前应用下的唯一id,即openid可以锁定“用户 + 应用”。应用的意思就是指某个公共号、某个小程序等,因此同一用户在不同的公共号、订阅号、小程序、小游戏都算是不同应用,它们各自的openId也不同。
  • unionId是用户在腾讯微信体系下的唯一id,即同一用户在不同的公共号、订阅号、小程序、小游戏下,unionId是相同的。

因此来说如果你想打通公共号(多数APP走公共号登录)和小程序登录的账号体系,共享用户数据,就要用unionId了,注意unionId是微信专属的,QQ并不走这套逻辑,QQ走自己的登录体系,虽然不同,但原理类似,只是微信更复杂(上面说的同一用户多应用的情况)。

openId也是很有用的,很多支付相关的或微信官方接口都是用openId做参数的,因此我们还需要经常在两个间转换,有兴趣的朋友可以去官网看看其他玩法(佩服微信强大的账号体系啊)。

获取用户信息的授权是什么?

如果我们只想页面上展示头像、昵称的话,而不打算入库,或操作用户的数据。完全可以在小程序上直接获取。

小程序的button组件: https://developers.weixin.qq.com/miniprogram/dev/component/button.html

// open-type 的合法值
// getUserInfo	获取用户信息,可以从bindgetuserinfo回调中获取到用户信息
<button open-type="getUserInfo" lang="zh_CN" bindgetuserinfo="onGotUserInfo">获取用户信息</button>
onGotUserInfo: function(e) {
    console.log(e.detail.errMsg)
    console.log(e.detail.userInfo)
    console.log(e.detail.rawData)
}
// 其中e.detail.userInfo中包含用户的头像昵称

 缺点是必须由用户手动触发,并有个系统提示。一旦允许,下一次有效期内就不会再弹了。

云开发的方案也不错

小程序的云开发中集成了获取登录信息的方案,直接就能在云函数里面获取用户openid等信息

附上文档地址: https://developers.weixin.qq.com/miniprogram/dev/wxcloud/basis/getting-started.html

基本方法如下:

// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
//获取用户的openid
exports.main = async(event, context) => {
 return event.userInfo; //返回用户信息
}

最后附上官方的架构图:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html   

                  

以上就是我整理的小程序登录,因为涉及支付,所以需要研究一下细节,以及如何使用,确定安全才放心,希望对大家有用。

原文地址:https://www.cnblogs.com/webARM/p/12443562.html