获取access_token https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
示例:
微信公众号中控服务器https://iyuu.cn:2130
前言
access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效
。
公众平台的API调用所需的access_token的使用及生成方式说明:
1、建议公众号开发者使用中控服务器统一获取和刷新access_token,其他业务逻辑服务器所使用的access_token均来自于该中控服务器,不应该各自去刷新,否则容易造成冲突,导致access_token覆盖而影响业务;
2、目前access_token的有效期通过返回的expire_in来传达,目前是7200秒之内的值。中控服务器需要根据这个有效时间提前去刷新新access_token。在刷新过程中,中控服务器可对外继续输出的老access_token,此时公众平台后台会保证在5分钟内,新老access_token都可用,这保证了第三方业务的平滑过渡;
3、access_token的有效时间可能会在未来有调整,所以中控服务器不仅需要内部定时主动刷新,还需要提供被动刷新access_token的接口,这样便于业务服务器在API调用获知access_token已超时的情况下,可以触发access_token的刷新流程。
公众号和小程序均可以使用AppID和AppSecret调用本接口来获取access_token。AppID和AppSecret可在“微信公众平台-开发-基本配置”页中获得(需要已经成为开发者,且帐号没有异常状态)。调用接口时,请登录“微信公众平台-开发-基本配置”提前将服务器IP地址添加到IP白名单中,点击查看设置方法,否则将无法调用成功。小程序无需配置IP白名单。
workerman源代码
start_token.php
源码
<?php
require_once __DIR__ . '/init.php';
require_once __DIR__ . '/WechatAccessToken.php';
use Workerman\Worker;
global $context;
// 日志文件
Worker::$logFile = SELF_PATH .'/Log/'.basename(__FILE__, '.php'). '.log';
$TokenService = new WechatAccessToken("http://0.0.0.0:2130", $context);
// 如果不是在根目录启动,则运行runAll方法
if (!defined('GLOBAL_START')) {
Worker::runAll();
}
WechatAccessToken.php
源码
<?php
use Workerman\Worker;
use Workerman\Lib\Timer;
use Workerman\Connection\TcpConnection;
/**
* 微信公众号的access_token中控服务
* Class WechatAccessToken
*/
class WechatAccessToken extends Worker
{
/**
* 进程名
* @var string
*/
public $name = 'WechatAccessTokenService';
/**
* 进程数
* @var int
*/
public $count = 1;
/**
* 进程启动时间
*
* @var int
*/
protected $_startTime = 0;
/**
* 微信接口
*/
const API_URL_PREFIX = 'https://api.weixin.qq.com/cgi-bin';
const AUTH_URL = '/token?';
/**
* 从微信获取到的access_token、expires_in
*/
private static $access_token = ''; //获取到的凭证
private static $expires_in = 0; //凭证有效时间(已经转换成了时间戳)
/**
* 微信参数expires_in的提前量(必须在5分钟以内)
* 官方文档:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
*/
const client_expires_before = 120; //客户端凭证有效时间的提前量(2分钟)
const server_expires_before = 60; //中控服务器凭证有效时间的提前量(3分钟),是在client_expires_before基础上再提前
/**
* 其他配置
* @var int
*/
private static $timerSUM = 0; //定时器执行次数
private static $API_OK = 0; //请求微信接口,成功次数
private static $API_ERROR = 0; //请求微信接口,失败次数
/**
* Redis实例
* @var Cache
*/
private $redis = null;
/**
* 用户配置
* @var array
*/
private $config = [];
/**
* 构造函数
* - 如果设置证书,会使用SSL协议
* @param string $socket_name
* @param array $context_option
*/
public function __construct($socket_name = '', array $context_option = array())
{
if (!empty($context_option)) {
$this->transport = 'ssl';
}
// 运行父方法
parent::__construct($socket_name, $context_option);
}
/**
* 运行,系统方法
*/
public function run()
{
// 设置 onWorkerStart 回调
$this->onWorkerStart = array($this, 'onWorkerStart');
// 设置 onMessage 回调
$this->onMessage = array($this, 'onMessage');
// 记录进程启动的时间
$this->_startTime = time();
// 强制单进程
$this->count = 1;
//运行父方法
parent::run();
}
/**
* 进程启动时回调
* @param WechatAccessToken $worker
* @throws Exception
*/
public function onWorkerStart($worker)
{
// 读取配置
$this->config = Config::get('application', 'iyuucn');
// 实例化redis缓存
$this->redis = new Cache();
//初始化微信参数
self::$access_token = $this->redis->get('WCH_access_token');
self::$expires_in = $this->redis->get('WCH_expires_in');
// 启动定时器
Timer::add(20, function () use ($worker) {
$worker->getAccessToken();
});
}
/**
* 当客户端通过连接发来数据时,触发的回调函数
* @param TcpConnection $connection
* @param $data
*/
public function onMessage($connection, $data)
{
$connection->send($this->index());
}
/**
* 获取access_token
* 官方文档:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
* @param bool $newToken 传入true可以强制刷新token
* @return string | null
* @throws Exception
*/
public function getAccessToken($newToken = false)
{
// 累加定时器执行次数
self::$timerSUM++;
if (self::valid_access_token() && !$newToken) {
//监测缓存token,如果被误删除,重新写入
if (empty($this->redis->get('WCH_access_token')) || empty($this->redis->get('WCH_expires_in'))) {
$this->redis->set('WCH_access_token', self::$access_token, self::$expires_in - time());
$this->redis->set('WCH_expires_in', self::$expires_in, self::$expires_in - time());
}
// 缓存有效,直接返回
return self::$access_token;
}
/**
* 失效或者强制获取access_token
*/
$options = $this->config;
if (!empty($options['appid']) && !empty($options['appsecret'])) {
$params = [
'grant_type'=> 'client_credential',
'appid' => $options['appid'],
'secret' => $options['appsecret']
];
$query_str = http_build_query($params);
$resp = HttpCurl::get(self::API_URL_PREFIX . self::AUTH_URL . $query_str);
$response = json_decode($resp);
if (!empty($response->access_token) && !empty($response->expires_in)) {
/**
* 凭证获取,成功: {"access_token":"ACCESS_TOKEN","expires_in":7200}
*/
self::$API_OK++;
//保存微信参数
self::$access_token = $response->access_token;
self::$expires_in = time() + $response->expires_in - self::client_expires_before;
//微信参数放入redis
$this->redis->set('WCH_access_token', self::$access_token, $response->expires_in);
$this->redis->set('WCH_expires_in', self::$expires_in, $response->expires_in);
return $response->access_token;
}
self::$API_ERROR++;
wLog('获取access_token失败:');
wLog($resp);
return null;
} else {
//缺少开发者参数
throw new Exception(__METHOD__ . "appid,appsecret not config");
}
}
/**
* 校验access_token是否过期
* @return bool
*/
private static function valid_access_token()
{
// 中控服务器需要提前获取凭证,然后放入缓存中(保障其他应用始终可以从缓存中拿到可用的access_token)
return self::$access_token && self::$expires_in && (self::$expires_in > (time() + self::server_expires_before));
}
/**
* 首页
* @return string
*/
public function index()
{
$s = self::$expires_in - time();
$str = '';
$str .= '系统启动:'. date("Y-m-d H:i:s", $this->_startTime) ."<br />";
$str .= '定时器执行:'. self::$timerSUM ."<br />";
$str .= '凭证Token有效期:'. $s ."秒<br />";
$str .= '刷新Token成功:'. self::$API_OK ."次<br />";
$str .= '刷新Token失败:'. self::$API_ERROR ."次<br />";
return $str;
}
}
Linux开机以守护进程方式运行
打开/etc/rc.local
,在exit 0
前添加类似以下代码
/绝对路径/php /磁盘/路径/start.php start -d