资讯专栏INFORMATION COLUMN

PHP实现微信小程序用户授权的工具类

seanHai / 815人阅读

摘要:事先准备工作申请一个小程序,申请地址传送门仔细阅读小程序的用户授权登陆官方文档用户授权登陆的流程仔细阅读微信用户数据解密的相关文档用户数据解密说明文档在小程序后台配置好相应的后端请求地址,路径是开发开发设置,如图小程序如果需要做多个

事先准备工作

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/authorization?param=" + 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("%s?appid=%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状态代码类
           
               
                                           
                       
                 

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/30953.html

相关文章

  • PHP实现支付宝小程序用户授权工具

    摘要:背景最近项目需要上线支付宝小程序,同时需要走用户的授权流程完成用户信息的存储,以前做过微信小程序的开发,本以为实现授权的过程是很简单的事情,但是再实现的过程中还是遇到了不少的坑,因此记录一下实现的过程学到的知识支付宝开放接口的调用模式以及实 背景 最近项目需要上线支付宝小程序,同时需要走用户的授权流程完成用户信息的存储,以前做过微信小程序的开发,本以为实现授权的过程是很简单的事情,但是...

    weapon 评论0 收藏0
  • 信小程序开发中二三事之网易云信IMSDK DEMO

    摘要:传统的网页编程采用的三剑客来实现,在微信小程序中同样有三剑客。观察者模式不难实现,重点是如何在微信小程序中搭配其特有的生命周期来使用。交互事件传统的事件传递类型有冒泡型与捕获型,微信小程序中自然也有。 本文由作者邹永胜授权网易云社区发布。 简介为了更好的展示我们即时通讯SDK强悍的能力,网易云信IM SDK微信小程序DEMO的开发就提上了日程。用产品的话说就是: 云信 IM 小程序 S...

    weij 评论0 收藏0
  • Canvas绘图在信小程序应用:生成个性化海报

    摘要:解析进到首页其实关键字在本地就随机取完了,在首页中的方法中就通过缓存了要画的元素,比如关键字这里是图片关键字解析语也是图片毕竟微信小程序的不支持字体等等。 一、Canvas应用的背景(个人理解)及基础语法 背景 从2012年开始,微信那个时候用户的积累的量已经非常大了,推出公众号,当然大屏智能手机在那个时候也流行,传统的大众媒体逐步消亡,像微信公众号这样的新媒体盛行。企业的广告投入开始...

    vpants 评论0 收藏0
  • 信小程序授权登录、解密unionId出错

    摘要:注没有在微信开放平台做开发者资质认证的就不要浪费时间了,没认证无法获取,认证费用元年,微信授权登录流程第一步获取用户临时登录凭证第二步获取加密过的数据和解密参数第三步把步骤一二中的传到开发者自己服务端第三步服务端获取到之后用方法请求如下微信 注:没有在微信开放平台做开发者资质认证的就不要浪费时间了,没认证无法获取unionId,认证费用300元/年,emmmm.... 微信授权登录流程...

    tinysun1234 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<