欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-05-14 14:19:33
事先准备工作 1.申请一个小程序,申请地址: 2.仔细阅读小程序的用户授权登陆官方文档: 3.仔细阅读微信用户数据解密的相关文档: 4.在小程序后台配置好相应的...

先准备工作

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

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

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

6.服务端准备一个用户授权的接口,假设接口链接为,此接口接受如下参数

  • 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;

实现步骤

用户授权时序图

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

关键代码

小程序端

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

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工具类

<?php
/**
 * created by phpstorm.
 * user: auser
 * time: 11:17
 */

namespace app\http\base\wx;


class wxbizdatahelper
{

  private $appid;
  private $seesionkey ;
  private $encrypteddata;
  private $iv;
  public function __construct($appid, $sessionkey,$encrypteddata, $iv)
  {
    $this->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工具类

<?php
/**
 * created by phpstorm.
 * user: auser
 * time: 10:38
 */

namespace app\http\base\wx;

use app\http\base\wx\prpcrypt;
use app\http\base\wx\errorcode;
use app\http\base\wx\pkcs7encoder;
class wxbizdatacrypt
{

  private $appid;
  private $sessionkey;

  /**
   * 构造函数
   * @param $sessionkey string 用户在小程序登录后获取的会话密钥
   * @param $appid string 小程序的appid
   */
  public function __construct( $appid, $sessionkey)
  {
    $this->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工具类

<?php
/**
 * created by phpstorm.
 * user: auser
 * time: 10:55
 */

namespace app\http\base\wx;

class prpcrypt
{
  public $key;

  public function __construct($key)
  {
    $this->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
/**
 * created by phpstorm.
 * user: auser
 * time: 10:33
 */

namespace app\http\base\wx;


class errorcode
{
  public static $ok = 0;
  public static $illegalaeskey = -41001;
  public static $illegaliv = -41002;
  public static $illegalbuffer = -41003;
  public static $decodebase64error = -41004;

}

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