php教程

超轻量级php框架startmvc

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

更新时间:2020-04-03 08:49:51 作者:startmvc
事先准备工作1.申请一个小程序,申请地址:传送门2.仔细阅读小程序的用户授权登陆官方

先准备工作

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


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

}

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

PHP 微信小程序用户授权 PHP 小程序用户授权