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

网友投稿 238 2023-11-10

先准备工作

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

1.申请一个小程序,申请地址:传送门

2.仔细阅读小程序的用户授权登陆官方文档: 《用户授权登陆的流程》

3.仔细阅读微信用户数据解密的相关文档: 《用户数据解密说明文档》

4.在小程序后台配置好相应的后端请求地址,路径是:开发---->开发设置,如图

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

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

code:微信登陆接口返回的登陆凭证,用户获取session_key iv:微信小程序登陆接口返回的向量,用于数据解密 encrypted_data : 微信获取用户信息接口的返回的用户加密数据,用于后端的接口解析 signature加密数据

接口返回的数据如下

?
1
2
3
4
5
6
7
8
{
"errcode": 200,
"msg": "SUCCESS",
"data": {
"uid": 34098,
"unionid": "xxx",
}
}

6.建表

1)用户表,其中比较重要的字段是union_id,因为我们是有多个小程序和公众号,因此使用这个来区分唯一的用户编号

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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中,作为全局参数

获取用户的授权信息

服务端

入口方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/**
* 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;
}

保存用户信息的方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/**
* 保存用户信息的方法
* @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获取用户信息

?
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 根据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工具类

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?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工具类

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<?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工具类

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?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状态代码类

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?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实现动态表单生成工具详解Thinkphp自定义代码生成工具及用法说明(附-)PHP的bz2压缩扩展工具详解七种PHP开发环境搭建工具PHP实现统计代码行数小工具php8扩展arginfo生成工具及工具初体验介绍

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:微信小程序实现登录注册界面
下一篇:微信小程序实现小型计算器
相关文章

 发表评论

暂时没有评论,来抢沙发吧~