PHP实现微信小程序用户授权的工具类示例
程序员文章站
2022-10-29 19:59:46
事先准备工作
1.申请一个小程序,申请地址:
2.仔细阅读小程序的用户授权登陆官方文档:
3.仔细阅读微信用户数据解密的相关文档:
4.在小程序后台配置好相应的...
事先准备工作
1.申请一个小程序,申请地址:
2.仔细阅读小程序的用户授权登陆官方文档:
3.仔细阅读微信用户数据解密的相关文档:
4.在小程序后台配置好相应的后端请求地址,路径是:开发---->开发设置,如图
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;
实现步骤
用户授权时序图
关键代码
小程序端
小程序端的获取用户信息流程
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; }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: 鸡腿怎么去腥味,你了解了吗?
下一篇: 四川雅安荥经特产,大部分人不知道这个