fix:更新已知bug,优化代码
This commit is contained in:
218
vendor/overtrue/socialite/src/Providers/WeChat.php
vendored
Normal file
218
vendor/overtrue/socialite/src/Providers/WeChat.php
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
<?php
|
||||
|
||||
namespace Overtrue\Socialite\Providers;
|
||||
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
use Overtrue\Socialite\Contracts;
|
||||
use Overtrue\Socialite\Exceptions;
|
||||
use Overtrue\Socialite\User;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* @see http://mp.weixin.qq.com/wiki/9/01f711493b5a02f24b04365ac5d8fd95.html [WeChat - 公众平台OAuth文档]
|
||||
* @see https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=&lang=zh_CN
|
||||
* [网站应用微信登录开发指南]
|
||||
*/
|
||||
class WeChat extends Base
|
||||
{
|
||||
public const NAME = 'wechat';
|
||||
|
||||
protected string $baseUrl = 'https://api.weixin.qq.com/sns';
|
||||
|
||||
protected array $scopes = ['snsapi_login'];
|
||||
|
||||
protected bool $withCountryCode = false;
|
||||
|
||||
protected ?array $component = null;
|
||||
|
||||
protected ?string $openid = null;
|
||||
|
||||
public function __construct(array $config)
|
||||
{
|
||||
parent::__construct($config);
|
||||
|
||||
if ($this->getConfig()->has('component')) {
|
||||
$this->prepareForComponent((array) $this->getConfig()->get('component'));
|
||||
}
|
||||
}
|
||||
|
||||
public function withOpenid(string $openid): self
|
||||
{
|
||||
$this->openid = $openid;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withCountryCode(): self
|
||||
{
|
||||
$this->withCountryCode = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function tokenFromCode(string $code): array
|
||||
{
|
||||
$response = $this->getTokenFromCode($code);
|
||||
|
||||
return $this->normalizeAccessTokenResponse($response->getBody());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,string> $componentConfig [Contracts\ABNF_ID => xxx, Contracts\ABNF_TOKEN => xxx]
|
||||
*/
|
||||
public function withComponent(array $componentConfig): self
|
||||
{
|
||||
$this->prepareForComponent($componentConfig);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getComponent(): ?array
|
||||
{
|
||||
return $this->component;
|
||||
}
|
||||
|
||||
protected function getAuthUrl(): string
|
||||
{
|
||||
$path = 'oauth2/authorize';
|
||||
|
||||
if (\in_array('snsapi_login', $this->scopes)) {
|
||||
$path = 'qrconnect';
|
||||
}
|
||||
|
||||
return $this->buildAuthUrlFromBase("https://open.weixin.qq.com/connect/{$path}");
|
||||
}
|
||||
|
||||
protected function buildAuthUrlFromBase(string $url): string
|
||||
{
|
||||
$query = \http_build_query($this->getCodeFields(), '', '&', $this->encodingType);
|
||||
|
||||
return $url.'?'.$query.'#wechat_redirect';
|
||||
}
|
||||
|
||||
protected function getCodeFields(): array
|
||||
{
|
||||
if (! empty($this->component)) {
|
||||
$this->with(\array_merge($this->parameters, ['component_appid' => $this->component[Contracts\ABNF_ID]]));
|
||||
}
|
||||
|
||||
return \array_merge([
|
||||
'appid' => $this->getClientId(),
|
||||
Contracts\RFC6749_ABNF_REDIRECT_URI => $this->redirectUrl,
|
||||
Contracts\RFC6749_ABNF_RESPONSE_TYPE => Contracts\RFC6749_ABNF_CODE,
|
||||
Contracts\RFC6749_ABNF_SCOPE => $this->formatScopes($this->scopes, $this->scopeSeparator),
|
||||
Contracts\RFC6749_ABNF_STATE => $this->state ?: \md5(\uniqid(Contracts\RFC6749_ABNF_STATE, true)),
|
||||
'connect_redirect' => 1,
|
||||
], $this->parameters);
|
||||
}
|
||||
|
||||
protected function getTokenUrl(): string
|
||||
{
|
||||
return \sprintf($this->baseUrl.'/oauth2%s/access_token', empty($this->component) ? '' : '/component');
|
||||
}
|
||||
|
||||
public function userFromCode(string $code): Contracts\UserInterface
|
||||
{
|
||||
if (\in_array('snsapi_base', $this->scopes)) {
|
||||
return $this->mapUserToObject($this->fromJsonBody($this->getTokenFromCode($code)));
|
||||
}
|
||||
|
||||
$token = $this->tokenFromCode($code);
|
||||
|
||||
$this->withOpenid($token['openid']);
|
||||
|
||||
$user = $this->userFromToken($token[$this->accessTokenKey]);
|
||||
|
||||
return $user->setRefreshToken($token[Contracts\RFC6749_ABNF_REFRESH_TOKEN])
|
||||
->setExpiresIn($token[Contracts\RFC6749_ABNF_EXPIRES_IN])
|
||||
->setTokenResponse($token);
|
||||
}
|
||||
|
||||
protected function getUserByToken(string $token): array
|
||||
{
|
||||
$language = $this->withCountryCode ? null : (isset($this->parameters['lang']) ? $this->parameters['lang'] : 'zh_CN');
|
||||
|
||||
$response = $this->getHttpClient()->get($this->baseUrl.'/userinfo', [
|
||||
'query' => \array_filter([
|
||||
Contracts\RFC6749_ABNF_ACCESS_TOKEN => $token,
|
||||
'openid' => $this->openid,
|
||||
'lang' => $language,
|
||||
]),
|
||||
]);
|
||||
|
||||
return $this->fromJsonBody($response);
|
||||
}
|
||||
|
||||
#[Pure]
|
||||
protected function mapUserToObject(array $user): Contracts\UserInterface
|
||||
{
|
||||
return new User([
|
||||
Contracts\ABNF_ID => $user['openid'] ?? null,
|
||||
Contracts\ABNF_NAME => $user[Contracts\ABNF_NICKNAME] ?? null,
|
||||
Contracts\ABNF_NICKNAME => $user[Contracts\ABNF_NICKNAME] ?? null,
|
||||
Contracts\ABNF_AVATAR => $user['headimgurl'] ?? null,
|
||||
Contracts\ABNF_EMAIL => null,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getTokenFields(string $code): array
|
||||
{
|
||||
return empty($this->component) ? [
|
||||
'appid' => $this->getClientId(),
|
||||
'secret' => $this->getClientSecret(),
|
||||
Contracts\RFC6749_ABNF_CODE => $code,
|
||||
Contracts\RFC6749_ABNF_GRANT_TYPE => Contracts\RFC6749_ABNF_AUTHORATION_CODE,
|
||||
] : [
|
||||
'appid' => $this->getClientId(),
|
||||
'component_appid' => $this->component[Contracts\ABNF_ID],
|
||||
'component_access_token' => $this->component[Contracts\ABNF_TOKEN],
|
||||
Contracts\RFC6749_ABNF_CODE => $code,
|
||||
Contracts\RFC6749_ABNF_GRANT_TYPE => Contracts\RFC6749_ABNF_AUTHORATION_CODE,
|
||||
];
|
||||
}
|
||||
|
||||
protected function getTokenFromCode(string $code): ResponseInterface
|
||||
{
|
||||
return $this->getHttpClient()->get($this->getTokenUrl(), [
|
||||
'headers' => ['Accept' => 'application/json'],
|
||||
'query' => $this->getTokenFields($code),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exceptions\InvalidArgumentException
|
||||
*/
|
||||
protected function prepareForComponent(array $component): void
|
||||
{
|
||||
$config = [];
|
||||
foreach ($component as $key => $value) {
|
||||
if (\is_callable($value)) {
|
||||
$value = \call_user_func($value, $this);
|
||||
}
|
||||
|
||||
switch ($key) {
|
||||
case Contracts\ABNF_ID:
|
||||
case Contracts\ABNF_APP_ID:
|
||||
case 'component_app_id':
|
||||
$config[Contracts\ABNF_ID] = $value;
|
||||
break;
|
||||
case Contracts\ABNF_TOKEN:
|
||||
case Contracts\RFC6749_ABNF_ACCESS_TOKEN:
|
||||
case 'app_token':
|
||||
case 'component_access_token':
|
||||
$config[Contracts\ABNF_TOKEN] = $value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (2 !== \count($config)) {
|
||||
throw new Exceptions\InvalidArgumentException('Please check your config arguments were available.');
|
||||
}
|
||||
|
||||
if (1 === \count($this->scopes) && \in_array('snsapi_login', $this->scopes)) {
|
||||
$this->scopes = ['snsapi_base'];
|
||||
}
|
||||
|
||||
$this->component = $config;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user