|
|
@@ -0,0 +1,282 @@
|
|
|
+<?php
|
|
|
+// +----------------------------------------------------------------------
|
|
|
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
|
|
+// +----------------------------------------------------------------------
|
|
|
+// | Copyright (c) 2016~2020 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);
|
|
|
+ };
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+}
|