微信小程序登录态实现与维护

先上一张官方登录流程图:

微信小程序登录态实现与维护_第1张图片

官方解释:

  1. 小程序调用wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。

  2. 开发者服务器以code换取 用户唯一标识openid 和 会话密钥session_key。

            之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。


接下来将以ThinkPHP3.2框架结合小程序讲解以上流程的简单实现。

按照第一步:通过小程序调用wx.login() 获取 临时登录凭证code,并调用接口传给我们的服务器

小程序端:

utils->utils.js文件 定义http请求方法

  const http = (url, callBack, method = 'GET', data = '') => {
  wx.request({
    url: url,
    data: data,
    method: method,
    header: {
      "content-Type": method == "GET" ? "application/json" : "application/x-www-form-urlencoded"
    },
    success: function (res) {
      callBack(res.data);
    },
    fail: function (error) {
      console.log(error);
    }
  })
}

module.exports = {
  http: http
}

app.js文件

// 引入文件
var util = require("./utils/util.js")
// 登录
wx.login({
   success: res => {
     // 发送 res.code 到后台换取 openId, sessionKey, unionId
     var loginUrl = this.globalData.baseUrl + '/v1/login'
     util.http(loginUrl, this.processLoginData, 'POST', { code: res.code })
   }
})

第二步:开发者服务器以code换取 用户唯一标识openid 和 会话密钥session_key,并将openid和session_key存入缓存中,之后生成一个随机字符串(session3rd)返回给小程序端用于用户登录标识,并设置一定时间过期

服务端代码:

    //登录接口
    public function login()
    {
        // 接收小程序端传递的code
        $code   = I('post.code');
        // 从数据库中获取appid和secret
        $appid  = M('WebConfig')->where(array('k' => 'weixin_appid'))->getField('v');
        $secret = M('WebConfig')->where(array('k' => 'weixin_secret'))->getField('v');
        // 调用微信服务器接口获取用户openid
        $url = "https://api.weixin.qq.com/sns/jscode2session?appid=" . $appid . "&secret=" . $secret . "&js_code=" . $code . "&grant_type=authorization_code";
        $res = curl_post($url);
        if ($res) {
            $res         = json_decode($res, true);
            $openid      = $res['openid'];
            $session_key = $res['session_key'];
        }
        //判断该用户是否已存在wx_user用户表
        $map['openid'] = $openid;
        $userinfo      = M('WxUser')->where($map)->find();
        //若不存则添加用户信息
        if (!$userinfo) {
            $timeStamp           = time();
            $info['openid']      = $openid;
            $info['create_time'] = $timeStamp;
            $info['update_time'] = $timeStamp;
            M('WxUser')->add($info);
        }
        // 生成session3rd
        // 此处为方便演示直接以时间戳代替,实际应用可自定义方法生成随机字符串
        $session3rd           = time();  
        $value['openid']      = $openid;
        $value['session_key'] = $session_key;
        // 缓存session3rd 并设置一个过期时间
        S($session3rd, $value, 3600);
        // 返回信息
        $data['session3rd'] = $session3rd;
        $data['msg']        = "登录成功";
        $data['code']       = 200;
        $this->response($data, 'json');
    }
function curl_post($url, $param = array())
{
    if (!is_array($param)) {
        throw new Exception("参数必须为array");
    }
    //转成json格式
    //$param=urldecode(json_encode($param));
    //var_dump($param);
    //$headerInfo=array('Content-Type:application/json');
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url); //设置访问的url地址
    curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置超时
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); //跟踪301
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回结果
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
    curl_setopt($ch, CURLOPT_POST, 1); //设置为POST方式
    curl_setopt($ch, CURLOPT_POSTFIELDS, $param); //设置参数
    //curl_setopt ($ch, CURLOPT_HTTPHEADER, $headerInfo);
    $r = curl_exec($ch);
    curl_close($ch);
    return $r;
}

以上基本实现微信小程序的一个简单登录流程。

下面是关于微信小程序用户数据的签名验证和加解密:

官方图:

微信小程序登录态实现与维护_第2张图片

通过上面的步骤我们事先通过 wx.login 登录流程获取到了会话密钥 session_key 并将它保存到了缓存中,session_key对于后面我们进行

签名校验以及数据加解密有着重要作用。

按照官方步骤:

1.通过调用接口(如 wx.getUserInfo)获取数据时,接口会同时返回 rawData、signature,其中 signature = sha1( rawData + session_key )

2.开发者将 signature、rawData 发送到开发者服务器进行校验。服务器利用用户对应的 session_key 使用相同的算法计算出签名 signature2 ,比对 signature 与 signature2 即可校验数据的完整性。

第一步:通过wx.getUserInfo获取rawData、signature等参数,并将一系列参数和我们此前获取到的session3rd传到我们的服务器中进行校验。

    // 获取用户信息
    wx.getSetting({
      withCredentials: true,
      success: res => {
        if (res.authSetting['scope.userInfo']) {
          // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
          wx.getUserInfo({
            success: res => {
              var that = this
              var userInfoUrl = that.globalData.baseUrl + "/v1/login"
                // 可以将 res 发送给后台解码出 unionId
                that.globalData.userInfo = res.userInfo

                var data = {
                  'rawData': res.rawData,
                  'signature': res.signature,
                  'iv': res.iv,
                  'encryptedData': res.encryptedData,
                  'session3rd': wx.getStorageSync('session3rd')
                }
                util.http(userInfoUrl, that.processuserInfoData, 'POST', data)

                // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
                // 所以此处加入 callback 以防止这种情况
                if (that.userInfoReadyCallback) {
                  that.userInfoReadyCallback(res)
                }

            }
          })
        }
      }
    })
  processuserInfoData: function (data) {
    console.log(data)
  },

第二步,服务器接收到参数rawData、signature,并通过session3rd取出之前我们保存的session_key与rawData进行sha1加密,即:signature = sha1( rawData + session_key )

    //更新用户信息
    public function userInfoUpdate()
    {
        //获取数据
        $iv            = I('post.iv');
        $code          = I('post.code');
        $signature     = I('post.signature');
        $encryptedData = I('post.encryptedData');
        $session3rd    = I('post.session3rd');
        $rawData       = $_POST['rawData']; // 这里不使用I方法获参,是因为I方法会过滤参数,导致rawData获取不准确

        //判断session3rd是否过期
        if (!S($session3rd)) {
            $result['msg']  = 'session3rd不存在';
            $result['code'] = 401;
            $this->response($result, 'json');
        }

        //数据签名校验
        $session3rd = S($session3rd);
        $signature2 = sha1($rawData . $session3rd['session_key']);
        if ($signature2 !== $signature) {
            $result['msg']  = '微信数据签名校验错误';
            $result['code'] = 401;
            $this->response($result, 'json');
        }

        //数据解密
        Vendor("Wx.wxBizDataCrypt");
        $appid   = "wx7667bca35c26f4e8";
        $pc      = new \WXBizDataCrypt($appid, $session3rd['session_key']);
        $errCode = $pc->decryptData($encryptedData, $iv, $data);

        if ($errCode == 0) {
            $arr               = json_decode($data, true);
            $openid            = $arr['openId'];
            $info['unionid']   = $arr['unionId'];
            $info['nickname']  = $arr['nickName'];
            $info['city']      = $arr['city'];
            $info['province']  = $arr['province'];
            $info['country']   = $arr['country'];
            $info['avatarUrl'] = $arr['avatarUrl'];
            $info['sex']       = $arr['gender'];

            //用户信息更新数据库
            $res = M('WxUser')->where("openid='$openid'")->save($info);
            if ($res) {
                $result['code'] = 201;
                $result['msg']  = '用户信息更新成功';
            } else {
                $result['code'] = 500;
                $result['msg']  = '用户信息更新失败';
            }
            $this->response($result, 'json');
        } else {
            $result['msg']  = '解密错误';
            $result['code'] = 401;
            $this->response($result, 'json');
        }
    }

上面的解密类(wxBizDataCrypt)可到官方文档下载:

https://developers.weixin.qq.com/miniprogram/dev/api/signature.html#wxchecksessionobject





你可能感兴趣的:(前端,后端,笔记,PHP,微信小程序)