diff --git a/crmeb/app/services/pc/LoginServices.php b/crmeb/app/services/pc/LoginServices.php index 78e15179e..66bf9cd9f 100644 --- a/crmeb/app/services/pc/LoginServices.php +++ b/crmeb/app/services/pc/LoginServices.php @@ -109,8 +109,8 @@ class LoginServices extends BaseServices throw new ValidateException('unionid不存在'); } if (!isset($wechatInfo['nickname'])) { - $wechatInfo = WechatAuthService::getUserInfo($wechatInfo['openid']); - if (!$wechatInfo['subscribe'] && !isset($wechatInfo['nickname'])) + $wechatInfo = WechatAuthService::oauth2Service()->getUserInfo($wechatInfo['openid'])->toArray(); + if (!isset($wechatInfo['nickname'])) throw new ValidateException('授权失败'); if (isset($wechatInfo['tagid_list'])) $wechatInfo['tagid_list'] = implode(',', $wechatInfo['tagid_list']); diff --git a/crmeb/app/services/user/UserServices.php b/crmeb/app/services/user/UserServices.php index 1c4854308..fb58949fd 100644 --- a/crmeb/app/services/user/UserServices.php +++ b/crmeb/app/services/user/UserServices.php @@ -1779,34 +1779,33 @@ class UserServices extends BaseServices foreach ($noBeOpenids as $openid) { try { $info = WechatService::getUserInfo($openid); + $info = is_object($info) ? $info->toArray() : $info; } catch (\Throwable $e) { $info = []; } if (!$info) continue; - if ($info['subscribe'] == 1) { - $data['nickname'] = $info['nickname'] ?? ''; - $data['headimgurl'] = $info['headimgurl'] ?? ''; - $userInfoData = $this->setUserInfo($data); - if (!$userInfoData) { - throw new AdminException('用户信息储存失败!'); - } - $data['uid'] = $userInfoData['uid']; - $data['subscribe'] = $info['subscribe']; - $data['unionid'] = $info['unionid'] ?? ''; - $data['openid'] = $info['openid'] ?? ''; - $data['sex'] = $info['sex'] ?? 0; - $data['language'] = $info['language'] ?? ''; - $data['city'] = $info['city'] ?? ''; - $data['province'] = $info['province'] ?? ''; - $data['country'] = $info['country'] ?? ''; - $data['subscribe_time'] = $info['subscribe_time'] ?? ''; - $data['groupid'] = $info['groupid'] ?? 0; - $data['remark'] = $info['remark'] ?? ''; - $data['tagid_list'] = isset($info['tagid_list']) && $info['tagid_list'] ? implode(',', $info['tagid_list']) : ''; - $data['add_time'] = $time; - $data['is_complete'] = 1; - $dataAll[] = $data; + $data['nickname'] = $info['nickname'] ?? ''; + $data['headimgurl'] = $info['headimgurl'] ?? ''; + $userInfoData = $this->setUserInfo($data); + if (!$userInfoData) { + throw new AdminException('用户信息储存失败!'); } + $data['uid'] = $userInfoData['uid']; + $data['subscribe'] = $info['subscribe'] ?? 1; + $data['unionid'] = $info['unionid'] ?? ''; + $data['openid'] = $info['openid'] ?? ''; + $data['sex'] = $info['sex'] ?? 0; + $data['language'] = $info['language'] ?? ''; + $data['city'] = $info['city'] ?? ''; + $data['province'] = $info['province'] ?? ''; + $data['country'] = $info['country'] ?? ''; + $data['subscribe_time'] = $info['subscribe_time'] ?? ''; + $data['groupid'] = $info['groupid'] ?? 0; + $data['remark'] = $info['remark'] ?? ''; + $data['tagid_list'] = isset($info['tagid_list']) && $info['tagid_list'] ? implode(',', $info['tagid_list']) : ''; + $data['add_time'] = $time; + $data['is_complete'] = 1; + $dataAll[] = $data; } if ($dataAll) { /** @var WechatUserServices $wechatUser */ diff --git a/crmeb/app/services/wechat/WechatServices.php b/crmeb/app/services/wechat/WechatServices.php index c1a958965..3f7a111c3 100644 --- a/crmeb/app/services/wechat/WechatServices.php +++ b/crmeb/app/services/wechat/WechatServices.php @@ -79,13 +79,13 @@ class WechatServices extends BaseServices public function auth($spreadId, $login_type) { try { - $wechatInfo = WechatAuthService::oauthService()->user()->getOriginal(); + $wechatInfo = WechatAuthService::oauth2Service()->oauth(); } catch (\Exception $e) { throw new ValidateException('授权失败' . $e->getMessage() . 'line' . $e->getLine()); } if (!isset($wechatInfo['nickname'])) { - $wechatInfo = WechatAuthService::getUserInfo($wechatInfo['openid']); - if (!$wechatInfo['subscribe'] && !isset($wechatInfo['nickname'])) + $wechatInfo = WechatAuthService::oauth2Service()->getUserInfo($wechatInfo['openid'])->toArray(); + if (!isset($wechatInfo['nickname'])) throw new ValidateException('授权失败'); if (isset($wechatInfo['tagid_list'])) $wechatInfo['tagid_list'] = implode(',', $wechatInfo['tagid_list']); @@ -129,13 +129,13 @@ class WechatServices extends BaseServices public function newAuth($spreadId, $login_type) { try { - $wechatInfo = WechatAuthService::oauthService()->user()->getOriginal(); + $wechatInfo = WechatAuthService::oauth2Service()->oauth(); } catch (\Exception $e) { throw new ValidateException('授权失败' . $e->getMessage() . 'line' . $e->getLine()); } if (!isset($wechatInfo['nickname'])) { - $wechatInfo = WechatAuthService::getUserInfo($wechatInfo['openid']); - if (!$wechatInfo['subscribe'] && !isset($wechatInfo['nickname'])) + $wechatInfo = WechatAuthService::oauth2Service()->getUserInfo($wechatInfo['openid'])->toArray(); + if (!isset($wechatInfo['nickname'])) throw new ValidateException('授权失败'); if (isset($wechatInfo['tagid_list'])) $wechatInfo['tagid_list'] = implode(',', $wechatInfo['tagid_list']); @@ -210,8 +210,8 @@ class WechatServices extends BaseServices */ public function silenceAuth($spread) { - $wechatInfoConfig = WechatAuthService::oauthService()->user()->getOriginal(); - $wechatInfo = WechatAuthService::getUserInfo($wechatInfoConfig['openid'])->toArray(); + $wechatInfoConfig = WechatAuthService::oauth2Service()->oauth(); + $wechatInfo = WechatAuthService::oauth2Service()->getUserInfo($wechatInfoConfig['openid'])->toArray(); $openid = $wechatInfoConfig['openid']; /** @var WechatUserServices $wechatUserServices */ $wechatUserServices = app()->make(WechatUserServices::class); @@ -258,9 +258,16 @@ class WechatServices extends BaseServices */ public function silenceAuthNoLogin($spread) { - $wechatInfoConfig = WechatAuthService::oauthService()->user()->getOriginal(); - $wechatInfo = WechatAuthService::getUserInfo($wechatInfoConfig['openid'])->toArray(); + $wechatInfoConfig = WechatAuthService::oauth2Service()->oauth(); $openid = $wechatInfoConfig['openid']; + try { + $wechatInfo = WechatAuthService::oauth2Service()->getUserInfo($wechatInfoConfig['openid'])->toArray(); + } catch (\Throwable $e) { + $createData = [$openid, [], $spread, '', 'wechat']; + $userInfoKey = md5($openid . '_' . time() . '_wechat'); + Cache::setTokenBucket($userInfoKey, $createData, 7200); + return ['auth_login' => 1, 'key' => $userInfoKey]; + } /** @var WechatUserServices $wechatUserServices */ $wechatUserServices = app()->make(WechatUserServices::class); $user = $wechatUserServices->getAuthUserInfo($openid, 'wechat'); @@ -328,7 +335,7 @@ class WechatServices extends BaseServices public function isSubscribe(int $uid) { if ($uid) { - $subscribe = $this->dao->value(['uid' => $uid], 'subscribe') ? true : false; + $subscribe = (bool)$this->dao->value(['uid' => $uid], 'subscribe'); } else { $subscribe = true; } diff --git a/crmeb/app/services/wechat/WechatUserServices.php b/crmeb/app/services/wechat/WechatUserServices.php index 9f9365e3d..fe0aa58d7 100644 --- a/crmeb/app/services/wechat/WechatUserServices.php +++ b/crmeb/app/services/wechat/WechatUserServices.php @@ -123,8 +123,17 @@ class WechatUserServices extends BaseServices public function updateUser($openid) { $userInfo = WechatService::getUserInfo($openid); - $userInfo['tagid_list'] = implode(',', $userInfo['tagid_list']); - if (!$this->dao->update($openid, $userInfo->toArray(), 'openid')) + $userInfo = is_object($userInfo) ? $userInfo->toArray() : $userInfo; + if (isset($userInfo['nickname']) && $userInfo['nickname']) { + $userInfo['nickname'] = filter_emoji($userInfo['nickname']); + } else { + mt_srand(); + $userInfo['nickname'] = 'wx' . rand(100000, 999999); + } + if (isset($userInfo['tagid_list'])) { + $userInfo['tagid_list'] = implode(',', $userInfo['tagid_list']); + } + if (!$this->dao->update($openid, $userInfo, 'openid')) throw new AdminException('更新失败'); return true; } @@ -137,6 +146,12 @@ class WechatUserServices extends BaseServices public function setNewUser($openid) { $userInfo = WechatService::getUserInfo($openid); + if (isset($userInfo['nickname']) && $userInfo['nickname']) { + $userInfo['nickname'] = filter_emoji($userInfo['nickname']); + } else { + mt_srand(); + $userInfo['nickname'] = 'wx' . rand(100000, 999999); + } if (!isset($userInfo['subscribe']) || !$userInfo['subscribe'] || !isset($userInfo['openid'])) throw new ValidateException('请关注公众号!'); $userInfo = $userInfo->toArray(); diff --git a/crmeb/crmeb/services/Mini3rdProgramService.php b/crmeb/crmeb/services/Mini3rdProgramService.php index ecce63548..e7188c219 100644 --- a/crmeb/crmeb/services/Mini3rdProgramService.php +++ b/crmeb/crmeb/services/Mini3rdProgramService.php @@ -13,7 +13,7 @@ namespace crmeb\services; use app\services\pay\PayNotifyServices; use crmeb\exceptions\ApiException; -use EasyWeChat\Foundation\Application; +use crmeb\services\easywechat\Application; use think\exception\ValidateException; use crmeb\utils\Hook; use think\Response; diff --git a/crmeb/crmeb/services/WechatOpenService.php b/crmeb/crmeb/services/WechatOpenService.php index c362d0ce9..6ea00e025 100644 --- a/crmeb/crmeb/services/WechatOpenService.php +++ b/crmeb/crmeb/services/WechatOpenService.php @@ -11,7 +11,7 @@ namespace crmeb\services; -use EasyWeChat\Foundation\Application; +use crmeb\services\easywechat\Application; class WechatOpenService { diff --git a/crmeb/crmeb/services/WechatService.php b/crmeb/crmeb/services/WechatService.php index a0373dc73..917169c9c 100644 --- a/crmeb/crmeb/services/WechatService.php +++ b/crmeb/crmeb/services/WechatService.php @@ -17,7 +17,7 @@ use app\services\wechat\WechatReplyServices; use crmeb\exceptions\AdminException; use app\services\pay\PayNotifyServices; use crmeb\utils\ApiErrorCode; -use EasyWeChat\Foundation\Application; +use crmeb\services\easywechat\Application; use EasyWeChat\Message\Article; use EasyWeChat\Message\Image; use EasyWeChat\Message\Material; @@ -27,6 +27,7 @@ use EasyWeChat\Message\Video; use EasyWeChat\Message\Voice; use EasyWeChat\Payment\Order; use EasyWeChat\Server\Guard; +use Symfony\Component\HttpFoundation\Request; use think\exception\ValidateException; use think\Response; use crmeb\utils\Hook; @@ -242,6 +243,17 @@ class WechatService return self::application()->oauth; } + /** + * 网页授权 + * @return easywechat\oauth2\wechat\WechatOauth2Provider + */ + public static function oauth2Service() + { + $request = app()->request; + self::application()->oauth2->setRequest(new Request($request->get(), $request->post(), [], [], [], $request->server(), $request->getContent())); + return self::application()->oauth2; + } + /** * 模板消息接口 * @return \EasyWeChat\Notice\Notice diff --git a/crmeb/crmeb/services/easywechat/Application.php b/crmeb/crmeb/services/easywechat/Application.php new file mode 100644 index 000000000..89b229eba --- /dev/null +++ b/crmeb/crmeb/services/easywechat/Application.php @@ -0,0 +1,46 @@ + +// +---------------------------------------------------------------------- + +namespace crmeb\services\easywechat; + + +use crmeb\services\easywechat\oauth2\wechat\WechatOauth2Provider; +use crmeb\services\easywechat\wechatlive\ProgramProvider as LiveProgramProvider; + + +/** + * Class Application + * @package crmeb\services\easywechat + * @property LiveProgramProvider $wechat_live + * @property WechatOauth2Provider $oauth2 + */ +class Application extends \EasyWeChat\Foundation\Application +{ + + /** + * @var string[] + */ + protected $providersNew = [ + LiveProgramProvider::class, + WechatOauth2Provider::class + ]; + + /** + * Application constructor. + * @param $config + */ + public function __construct($config) + { + $this->providers = array_merge($this->providers, $this->providersNew); + parent::__construct($config); + } + +} diff --git a/crmeb/crmeb/services/easywechat/oauth2/wechat/WechatOauth.php b/crmeb/crmeb/services/easywechat/oauth2/wechat/WechatOauth.php new file mode 100644 index 000000000..0dcca66ff --- /dev/null +++ b/crmeb/crmeb/services/easywechat/oauth2/wechat/WechatOauth.php @@ -0,0 +1,282 @@ + +// +---------------------------------------------------------------------- + +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); + }; + }; + } + + +} diff --git a/crmeb/crmeb/services/easywechat/oauth2/wechat/WechatOauth2Provider.php b/crmeb/crmeb/services/easywechat/oauth2/wechat/WechatOauth2Provider.php new file mode 100644 index 000000000..5803f0da5 --- /dev/null +++ b/crmeb/crmeb/services/easywechat/oauth2/wechat/WechatOauth2Provider.php @@ -0,0 +1,51 @@ + +// +---------------------------------------------------------------------- + +namespace crmeb\services\easywechat\oauth2\wechat; + + +use crmeb\services\SystemConfigService; +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use think\facade\Request; + +/** + * 微信网页授权 + * Class WechatOauthProvider + * @package crmeb\services\easywechat\oauth\wechat + * @method oauth(string $code = '') code授权获取acces_token openid + * @method getUserInfo($openId, $lang = 'zh_CN') openid 获取用户信息 + * @method setRequest(Request $request) 设置request对象 + */ +class WechatOauth2Provider implements ServiceProviderInterface +{ + + /** + * + * @param Container $pimple + */ + public function register(Container $pimple) + { + $request = app('request'); + $wechat = SystemConfigService::more(['wechat_appid', 'wechat_app_appid', 'wechat_app_appsecret', 'wechat_appsecret']); + if ($request->isApp()) { + $appId = isset($wechat['wechat_app_appid']) ? trim($wechat['wechat_app_appid']) : ''; + $appsecret = isset($wechat['wechat_app_appsecret']) ? trim($wechat['wechat_app_appsecret']) : ''; + } else { + $appId = isset($wechat['wechat_appid']) ? trim($wechat['wechat_appid']) : ''; + $appsecret = isset($wechat['wechat_appsecret']) ? trim($wechat['wechat_appsecret']) : ''; + } + + $pimple['oauth2'] = function ($pimple) use ($appId, $appsecret) { + return new WechatOauth($pimple['access_token'], $appId, $appsecret); + }; + } +} diff --git a/crmeb/public/index.html b/crmeb/public/index.html index 7d7e71a10..917564549 100644 --- a/crmeb/public/index.html +++ b/crmeb/public/index.html @@ -1,2 +1,2 @@