AccessToken.php
4.52 KB
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
<?php
namespace common\exts\wechat;
use Yii;
use yii\log\Logger;
use common\helpers\Log as AppLog;
use common\services\redis\RedisConfig;
use common\exts\wechat\Log as WxLog;
use stdClass;
use function date;
use function time;
/**
*
* Class AccessToken
*
* 当前实例返回 APP 的 AccessToken 非用户授权的 access token
*
* 过期时间 7200 秒
* 自动管理 access token , 过期会重新获取.
* 该模块 会 调用 yii 的 cache 模块缓存相关数据.
*
* @官方文档说明:
* access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token。
* 开发者需要进行妥善保存。
* access_token的存储至少要保留512个字符空间。
* access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。
*
*
* 公众平台的API调用所需的access_token的使用及生成方式说明:
* 1、为了保密appsecrect,第三方需要一个access_token获取和刷新的中控服务器。
* 而其他业务逻辑服务器所使用的access_token均来自于该中控服务器,不应该各自去刷新,
* 否则会造成access_token覆盖而影响业务;
*
* 2、目前access_token的有效期通过返回的expire_in来传达,
* 目前是7200秒之内的值。中控服务器需要根据这个有效时间提前去刷新新access_token。
* 在刷新过程中,中控服务器对外输出的依然是老access_token,此时公众平台后台会保证在刷新短时间内,
* 新老access_token都可用,这保证了第三方业务的平滑过渡;
*
* 3、access_token的有效时间可能会在未来有调整,所以中控服务器不仅需要内部定时主动刷新,
* 还需要提供被动刷新access_token的接口,这样便于业务服务器在API调用获知access_token已超时的情况下,可以触发access_token的刷新流程。
*
*
* @package common\exts\wechat
*/
class AccessToken
{
/**
* 用来保存 Access Token 的唯一标识,禁止修改.
*/
//const STORE_ID = 'WECHAT_ACCESS_TOKEN';
const TOKEN_URL = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential';
private static function myLog($str)
{
AppLog::DEBUG($str);
}
/**
* @param $appId
* @param $appSecret
* @param bool $forceRefresh
* @return string
*/
public static function get($appId, $appSecret, $forceRefresh = false)
{
$redis = Yii::$app->redis;
$storeId = RedisConfig::WECHAT_ACCESS_TOKEN_PREFIX . $appId;
// access_token 应该全局存储与更新,
$data = false;
if ($redis->exists($storeId)) {
$data = json_decode($redis->get($storeId));
}
if (!isset($data->access_token) || !isset($data->expire_time)) {
$data = new stdClass();
$data->expire_time = 0;
$data->access_token = '';
}
$time = time();
//如果当前日期已经超过了过期时间,那么重新获取access token
if ($time > $data->expire_time || true == $forceRefresh) {
// 如果是企业号用以下URL获取access_token
// $url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={$this->appId}&corpsecret={$this->appSecret}";
$url = self::TOKEN_URL."&appid={$appId}&secret={$appSecret}";
$res = json_decode(Http::get($url));
if (isset($res->access_token)) {
$data->expire_time = $time + 7000;
$data->access_token = $res->access_token;
$redis->set($storeId, json_encode($data));
$redis->expireat($storeId, $data->expire_time); // 到期自动删除
// 跟踪日志
if ($forceRefresh) {
self::myLog("AccessToken强制刷新: access_token=[" . $data->access_token . "], expire_time=["
. $data->expire_time . "], 修改时间=[" . date("Y-m-d H:i:s", time()) . "], storeId[" . $storeId . "]");
} else {
self::myLog("AccessToken正常刷新: access_token=[" . $data->access_token . "], expire_time=["
. $data->expire_time . "], 修改时间=[" . date("Y-m-d H:i:s", time()) . "], storeId[" . $storeId . "]");
}
return $data->access_token;
} else {
Yii::getLogger()->log(json_encode($res), Logger::LEVEL_ERROR);
}
} else {
return $data->access_token;
}
}
}