侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

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

前端  /  管理员 发布于 5年前   660

事先准备工作

1.申请一个小程序,申请地址:传送门
2.仔细阅读小程序的用户授权登陆官方文档: 《用户授权登陆的流程》
3.仔细阅读微信用户数据解密的相关文档: 《用户数据解密说明文档》
4.在小程序后台配置好相应的后端请求地址,路径是:开发---->开发设置,如图


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

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

  • 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状态代码类

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

您可能感兴趣的文章:

  • php版微信公众平台之微信网页登陆授权示例
  • PHP实现微信网页授权开发教程
  • 微信网页授权(OAuth2.0) PHP 源码简单实现
  • weiphp微信公众平台授权设置
  • 基于php的微信公众平台开发入门实例
  • php判断页面是否是微信打开的示例(微信打开网页)
  • PHP对接微信公众平台消息接口开发流程教程
  • php实现微信公众平台账号自定义菜单类
  • PHP版微信第三方实现一键登录及获取用户信息的方法
  • PHP微信网页授权的配置文件操作分析


  • 上一条:
    vue分页插件的使用方法
    下一条:
    PHP后台实现微信小程序登录
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 使用 Alpine.js 排序插件对元素进行排序(0个评论)
    • 在js中使用jszip + file-saver实现批量下载OSS文件功能示例(0个评论)
    • 在vue中实现父页面按钮显示子组件中的el-dialog效果(0个评论)
    • 使用mock-server实现模拟接口对接流程步骤(0个评论)
    • vue项目打包程序实现把项目打包成一个exe可执行程序(0个评论)
    • 近期文章
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • Laravel 11.15版本发布 - Eloquent Builder中添加的泛型(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2017-06
    • 2017-07
    • 2017-08
    • 2017-09
    • 2017-10
    • 2017-11
    • 2018-03
    • 2018-04
    • 2018-05
    • 2018-06
    • 2018-09
    • 2018-11
    • 2018-12
    • 2019-02
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2021-04
    • 2021-05
    • 2021-07
    • 2021-08
    • 2021-09
    • 2021-10
    • 2021-11
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-06
    • 2023-07
    • 2023-09
    • 2023-10
    • 2023-11
    • 2023-12
    • 2024-01
    • 2024-02
    • 2024-03
    • 2024-04
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客