You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

282 lines
7.0 KiB

<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2023 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
namespace crmeb\services\easywechat\oauth2\wechat;
use Doctrine\Common\Cache\Cache;
use Doctrine\Common\Cache\FilesystemCache;
use EasyWeChat\Core\AbstractAPI;
use EasyWeChat\Core\AccessToken;
use EasyWeChat\Core\Exceptions\HttpException;
use EasyWeChat\Core\Http;
use EasyWeChat\Support\Collection;
use GuzzleHttp\Psr7\Uri;
use Psr\Http\Message\RequestInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Class WechatOauth
* @package crmeb\services\easywechat\oauth\wechat
*/
class WechatOauth extends AbstractAPI
{
/**
* 通过code获取网页授权access_token
*/
const API_OAUTH_ACCESS_TOKEN = 'https://api.weixin.qq.com/sns/oauth2/access_token';
/**
* 检验授权凭证(access_token)是否有效
*/
const API_OAUTH_CHECK_TOKEN = 'https://api.weixin.qq.com/sns/auth';
/**
* 刷新access_token
*/
const API_OAUTH_REFRESH_TOKEN = 'https://api.weixin.qq.com/sns/oauth2/refresh_token';
/**
* 获取用户信息
*/
const API_OAUTH_GET_USER_INFO = 'https://api.weixin.qq.com/sns/userinfo';
/**
* App ID.
*
* @var string
*/
protected $appId;
/**
* App secret.
*
* @var string
*/
protected $secret;
/**
* Cache.
*
* @var Cache
*/
protected $cache;
protected $openid;
/**
* @var Request
*/
protected $request;
/**
* Query name.
*
* @var string
*/
protected $queryName = 'access_token';
/**
* Response Json key name.
*
* @var string
*/
protected $tokenJsonKey = 'access_token';
/**
* Response Json key name.
*
* @var string
*/
protected $refreshTokenJsonKey = 'refresh_token';
/**
* Cache key prefix.
*
* @var string
*/
protected $prefix = 'easywechat.common.oauth.access_token.';
/**
* WechatOauth constructor.
* @param AccessToken $accessToken
* @param $appId
* @param $appSecret
*/
public function __construct(AccessToken $accessToken, $appId, $appSecret)
{
parent::__construct($accessToken);
$this->appId = $appId;
$this->secret = $appSecret;
}
/**
* @param Request $request
* @return $this
*/
public function setRequest(Request $request)
{
$this->request = $request;
return $this;
}
/**
* 获取code
* @return mixed
*/
public function getCode()
{
return $this->request->get('code');
}
/**
* 授权获取token
* @param string $code
* @return false|mixed
* @throws HttpException
*/
public function oauth(string $code = '')
{
$params = [
'appid' => $this->appId,
'secret' => $this->secret,
'code' => $code ?: $this->getCode(),
'grant_type' => 'authorization_code',
];
$http = new Http();
$token = $http->parseJSON($http->get(self::API_OAUTH_ACCESS_TOKEN, $params));
if (empty($token[$this->tokenJsonKey])) {
throw new HttpException('Request AccessToken fail. response: ' . json_encode($token, JSON_UNESCAPED_UNICODE));
}
$this->setCache($token);
return $token;
}
/**
* 刷新token
* @param string $refresh_token
* @return false|mixed
* @throws HttpException
*/
public function refreshToken(string $refresh_token)
{
$params = [
'appid' => $this->appId,
'refresh_token' => $refresh_token,
'grant_type' => 'refresh_token',
];
$http = new Http();
$token = $http->parseJSON($http->get(self::API_OAUTH_REFRESH_TOKEN, $params));
if (empty($token[$this->tokenJsonKey])) {
throw new HttpException('Request AccessToken fail. response: ' . json_encode($token, JSON_UNESCAPED_UNICODE));
}
$this->setCache($token);
return $token;
}
/**
* 获取用户信息
* @param $openId
* @param string $lang
* @return Collection|null
* @throws HttpException
*/
public function getUserInfo($openId, $lang = 'zh_CN')
{
$params = [
'openid' => $openId,
'lang' => $lang,
];
$this->openid = $openId;
return $this->parseJSON('get', [self::API_OAUTH_GET_USER_INFO, $params]);
}
/**
* 获取token
* @param false $forceRefresh
* @return bool|mixed|string
* @throws HttpException
*/
public function getToken($forceRefresh = false)
{
$cacheKey = $this->prefix;
$cached = $this->getCache()->fetch($cacheKey . $this->tokenJsonKey . $this->openid);
if ($forceRefresh || !$cached) {
$refreshCached = $this->getCache()->fetch($cacheKey . $this->refreshTokenJsonKey . $this->openid);
if ($refreshCached) {
$token = $this->refreshToken($refreshCached);
return $token[$this->tokenJsonKey];
}
return '';
}
return $cached;
}
/**
* 保存token信息
* @param $token
* @return bool
*/
public function setCache($token)
{
$cacheKey = $this->prefix;
// XXX: T_T... 7200 - 1500
$this->getCache()->save($cacheKey . $this->tokenJsonKey . $token['openid'], $token[$this->tokenJsonKey], $token['expires_in'] - 1500);
$this->getCache()->save($cacheKey . $this->refreshTokenJsonKey . $token['openid'], $token[$this->refreshTokenJsonKey], 30 * 24 * 3600);
return true;
}
/**
* Return the cache manager.
*
* @return \Doctrine\Common\Cache\Cache
*/
public function getCache()
{
return $this->cache ?: $this->cache = new FilesystemCache(sys_get_temp_dir());
}
/**
* Attache access token to request query.
*
* @return \Closure
*/
protected function accessTokenMiddleware()
{
return function (callable $handler) {
return function (RequestInterface $request, array $options) use ($handler) {
$token = $this->getToken();
if (!$token) {
return $handler($request, $options);
}
$request = $request->withUri(Uri::withQueryValue($request->getUri(), $this->queryName, $token));
return $handler($request, $options);
};
};
}
}