频道栏目
首页 > 微信学院 > 微信小程序 > 正文
PHP实现微信小程序用户授权的工具类
2019-12-07 11:19:40           
收藏   我要投稿

事先准备工作

1.申请一个小程序,申请地址:传送门

2.仔细阅读小程序的用户授权登陆官方文档:《用户授权登陆的流程》

3.仔细阅读微信用户数据解密的相关文档:《用户数据解密说明文档》

4.在小程序后台配置好相应的后端请求地址,路径是:开发---->开发设置,如图

5.小程序如果需要做多个小程序的打通,还需要在微信开放平台绑定到开发者账号下面, 如果不需要union_id请忽略

6.服务端准备一个用户授权的接口,假设接口链接为http://test.dev.com/user/auth...,此接口接受如下参数

  • code:微信登陆接口返回的登陆凭证,用户获取session_key
  • iv:微信小程序登陆接口返回的向量,用于数据解密
  • encrypted_data : 微信获取用户信息接口的返回的用户加密数据,用于后端的接口解析
  • signature加密数据

    接口返回的数据如下

    {
        "errcode": 200,
        "msg": "SUCCESS",
        "data": {
            "uid": 34098,
            "unionid": "xxx",
        }
    }

    6.建表

    1)用户表,其中比较重要的字段是union_id,因为我们是有多个小程序和公众号,因此使用这个来区分唯一的用户编号

    DROP TABLE IF EXISTS `jz_wxa_user`;
    CREATE TABLE `jz_wxa_user` (
      `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
      `uid` bigint(18) DEFAULT NULL,
      `openid` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT 'openid',
      `user_name` varchar(100) CHARACTER SET utf8mb4 DEFAULT '',
      `nick_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '用户昵称',
      `sex` enum('0','1') CHARACTER SET utf8 DEFAULT '1' COMMENT '性别',
      `avatar` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '用户头像',
      `province` varchar(100) CHARACTER SET utf8 DEFAULT NULL COMMENT '省份',
      `city` varchar(100) CHARACTER SET utf8 DEFAULT NULL COMMENT '城市',
      `country` varchar(100) CHARACTER SET utf8 DEFAULT NULL COMMENT '国家',
      `wx_union_id` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '公众平台的唯一id',
      `from_url` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '来源url',
      `created_at` timestamp NULL DEFAULT NULL,
      `updated_at` timestamp NULL DEFAULT NULL,
      `from_appid` varchar(30) COLLATE utf8mb4_unicode_ci DEFAULT 'wx95fc895bebd3743b' COMMENT '来源appid',
      `wx_header` varchar(150) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '微信头像',
      `gh_openid` varchar(60) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '微信公众号openid',
      `phone` varchar(30) CHARACTER SET utf8 DEFAULT '' COMMENT '手机号码',
      PRIMARY KEY (`id`),
      KEY `idx_uid_union_id` (`uid`,`wx_union_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

    实现步骤

    用户授权时序图

    关键代码

    小程序端

    小程序端的获取用户信息流程

    1)调用login方法获取code

    2)调用getUserInfo方法获取用户的加密数据

    3)调用后端的用户授权接口将用户信息保存到服务端

    4)保存后端接口返回的uid和unionid到localstorage中,作为全局参数

    获取用户的授权信息

    getUid:function(cf){
        var that = this
        wx.login({
          success: function (ress) {
            var code = ress.code 
            wx.getUserInfo({ 
              withCredentials: true,          
              success: function (res) {
                that.globalData.userInfo = res.userInfo;
                that.authorize(code, res.signature, res.iv, res.rawData, res.encryptedData, cf)
              }
            })
          }
        })
      },
      authorize: function (code, signature, iv, rawData, encryptedData, cf) {
        var that =this
        var dataobj = {
          code: code,
          signature: signature,
          iv: iv,
          raw_data: rawData,
          encrypted_data: encryptedData
        }
        console.log("code:",code)
        var param = JSON.stringify(dataobj)
        param = that.Encrypt(param)
        var url = that.data.API_DOMAIN2 + "/user/authorizationparam=" + param
        wx.request({
          url: url,
          method: "GET",
          header: {
            'content-type': 'application/json'
          },
          success: function (res) {
            if (res.data.errcode == 200) {
              wx.hideToast()       
              wx.setStorage({
                key: "uid",
                data: res.data.data.uid,
                success: function () {
                  if (cf) {
                    typeof cf == "function" && cf(res.data.data.uid)
                  }
                }
              })
            } else {
              that.exceptionHandle('uid', url, res.data.errcode, res.data.msg)
            }
          }
        })
      },

    服务端

    入口方法

    /**
         * api接口开发
         * 获取详情的接口
         * @param $uid 用户编号
         * @param $iv 向量
         * @param $encryptedData 微信加密的数据
         * @param $rawData 判断是否为今天
         * @param $signature 签名
         * @return array
         */
        public static function authorization($appid,$appsecret,$code,$iv,$encryptedData,$rawData,$signature){
            $result = self::decodeWxData($appid,$appsecret,$code,$iv,$encryptedData);
            if($result['errcode'] != 200){
                return $result;
            }
            //处理微信授权的逻辑
            $wxUserData = $result['data'];
            error_log("authorization data=============>");
            error_log(json_encode($wxUserData));
            $uid = WxaUserService::regWxaUser($wxUserData);
            $data['uid'] = $uid['uid'];
            $data['unionid'] =  $uid['unionid'];
            $result['data'] = $data;
            return $result;
        }
        
        /**
         * 解密微信的数据
         * @param $code wx.login接口返回的code
         * @param $iv wx.getUserInfo接口或者wx.getWeRunData返回的iv
         * @param $encryptedData wx.getUserInfo接口或者wx.getWeRunData返回的加密数据
         * @return array
         */
        public static function decodeWxData($appid,$appsecret,$code,$iv,$encryptedData){
            $sessionKeyUrl = sprintf('%sappid=%s&secret=%s&js_code=%s&grant_type=authorization_code',config('param.wxa_user_info_session_key_url'),$appid,$appsecret,$code);
            $rtnJson = curlRequest($sessionKeyUrl);
            $data = json_decode($rtnJson,true);
            error_log('authorization wx return data========>');
            error_log($rtnJson);
            if(isset($data['errcode'])){
                return $data;
            }
            $sessionKey = $data['session_key'];
            $wxHelper = new WxBizDataHelper($appid,$sessionKey,$encryptedData,$iv);
            $data['errcode'] = 200;
            $data['data'] = [];
            if(!$wxData = $wxHelper->getData()){
                $data['errcode'] = -1;
            }else{
                error_log('current wx return data is =========>'.json_encode($wxData));
                $data['data'] = $wxData;
            }
            return $data;
        }

    保存用户信息的方法

    /**
         * 保存用户信息的方法
         * @param $wxaUserData
         * @param $regFromGh 表示是否从公众号进行注册
         */
        public function regWxaUser($wxaUserData,$regFromGh = false)
        {
            $value = $wxaUserData['unionId'];
            $key = getCacheKey('redis_key.cache_key.zset_list.lock') . $value;
            $newExpire = RedisHelper::getLock($key);
            $data =  $this->storeWxaUser($wxaUserData,$regFromGh);
            RedisHelper::releaseLock($key, $newExpire);
            return $data;
        }
        
        /**
         * 保存信息
         * @param $wxaUserData
         * @return mixed
         */
        public function storeWxaUser($wxaUserData,$regFromGh = false)
        {
            $wxUnionId = $wxaUserData['unionId'];
            if (!$user = $this->getByWxUnionId($wxUnionId)) {
                $getAccountDataStartTime = time();
                //这里是因为需要统一账户获取uid,所以这个是用户中心的接口,如果没有这个流程,则直接使用数据
                if($accountData = AccountCenterHelper::regWxaUser($wxaUserData)){
                    $getAccountDataEndTime = time();
                    $accountRegTime = $getAccountDataEndTime - $getAccountDataStartTime;
                    error_log("reg user spend time is ===================>" . $accountRegTime);
                    $user = [
                        'uid' => $accountData['uid'],
                        'user_name' => $accountData['user_name'],
                        'nick_name' => $wxaUserData['nickName'],
                        'sex' => $accountData['sex'],
                        'wx_union_id' => $accountData['wx_union_id'],
                        'avatar' => isset($accountData['avatar'])$accountData['avatar']:"",
                        'from_appid' => $accountData['from_appid'],
                        'province' => $wxaUserData['province'],
                        'city' => $wxaUserData['city'],
                        'country' => $wxaUserData['country'],
                        'openid' => $wxaUserData['openId'],
                        'wx_header' => isset($wxaUserData['avatarUrl'])$wxaUserData['avatarUrl']:"",
                        'gh_openid' => $regFromGh$wxaUserData['openId']:"",
                    ];
                    error_log("insert data=============>" . json_encode($user));
                    $user = $this->store($user);
                    $regApiUserEndTime = time();
                    error_log(" reg api user spend time================>" . ($regApiUserEndTime - $getAccountDataEndTime));
                    error_log(" after insert data=============>" . json_encode($user));
                }
            }else{
                if(!$user['wx_header']){
                    $updateData = [
                        'id' => $user['id'],
                        'uid' => $user['uid'],
                        'wx_header' => $wxaUserData['avatarUrl'],
                    ];
                    $this->update($updateData);
                }
                //同步用户的openid
                if($wxaUserData['openId'] != $user['openid']){
                    $updateData = [
                        'id' => $user['id'],
                        'uid' => $user['uid'],
                        'openid' => $wxaUserData['openId'],
                    ];
                    $this->update($updateData);
                }
            }
            $data['uid'] = $user['uid'];
            $data['unionid'] = $wxUnionId;
            return $data;
        }

    根据unionid获取用户信息

    /**
         * 根据unionid获取用户信息
         */
        public function getByWxUnionId($unionId)
        {
            $cacheKey = getCacheKey('redis_key.cache_key.wxa_user.info') . $unionId;
            $value = $this->remember($cacheKey, function () use ($unionId) {
                $userInfo = WxaUser::where('wx_union_id', $unionId)->first();
                $userInfo = $this->compactUserInfo($userInfo);
                return $userInfo;
            });
            return $value;
        }

    WxBizDataHelper工具类

    appid = $appid;
            $this->seesionKey = $sessionKey;
            $this->encryptedData = $encryptedData;
            $this->iv = $iv;
        }
    
        public function getData(){
            $pc = new WXBizDataCrypt($this->appid, $this->seesionKey);
            $json = '';
            $errCode = $pc->decryptData($this->encryptedData, $this->iv, $json);
            $data = [];
            if ($errCode == 0) {
                $data = json_decode($json,true);
            }
            return $data;
        }
    
    
    }

    WXBizDataCrypt工具类

    sessionKey = $sessionKey;
            $this->appid = $appid;
        }
    
    
        /**
         * 检验数据的真实性,并且获取解密后的明文.
         * @param $encryptedData string 加密的用户数据
         * @param $iv string 与用户数据一同返回的初始向量
         * @param $data string 解密后的原文
         *
         * @return int 成功0,失败返回对应的错误码
         */
        public function decryptData( $encryptedData, $iv, &$data )
        {
            if (strlen($this->sessionKey) != 24) {
                return ErrorCode::$IllegalAesKey;
            }
            $aesKey=base64_decode($this->sessionKey);
    
    
            if (strlen($iv) != 24) {
                return ErrorCode::$IllegalIv;
            }
            $aesIV=base64_decode($iv);
    
            $aesCipher=base64_decode($encryptedData);
    
            $pc = new Prpcrypt($aesKey);
            $result = $pc->decrypt($aesCipher,$aesIV);
    
            if ($result[0] != 0) {
                return $result[0];
            }
    
            $dataObj=json_decode( $result[1] );
            if( $dataObj  == NULL )
            {
                return ErrorCode::$IllegalBuffer;
            }
            if( $dataObj->watermark->appid != $this->appid )
            {
                return ErrorCode::$IllegalBuffer;
            }
            $data = $result[1];
            return ErrorCode::$OK;
        }
    
    }

    Prpcrypt工具类

    key = $key;
        }
    
        /**
         * 对密文进行解密
         * @param string $aesCipher 需要解密的密文
         * @param string $aesIV 解密的初始向量
         * @return string 解密得到的明文
         */
        public function decrypt($aesCipher, $aesIV)
        {
    
            try {
                $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
                mcrypt_generic_init($module, $this->key, $aesIV);
                //解密
                $decrypted = mdecrypt_generic($module, $aesCipher);
                mcrypt_generic_deinit($module);
                mcrypt_module_close($module);
            } catch (Exception $e) {
                return array(ErrorCode::$IllegalBuffer, null);
            }
    
    
            try {
                $result = PKCS7Encoder2::decode($decrypted);
            } catch (Exception $e) {
                //print $e;
                return array(ErrorCode::$IllegalBuffer, null);
            }
            return array(0, $result);
        }
    }

    ErrorCode状态代码类

点击复制链接 与好友分享!回本站首页
相关TAG标签 PHP 微信 小程序
上一篇:从0到1上线一个微信小程序
下一篇:微信小程序关于父子组件一些心得
相关文章
图文推荐
点击排行

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 全峰安全联盟--致力于做实用的IT技术学习网站