Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

添加企微第三方应用相关兼容 #2601

Merged
merged 7 commits into from
Oct 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 49 additions & 14 deletions src/OpenWork/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
use EasyWeChat\OpenWork\Contracts\SuiteTicket as SuiteTicketInterface;
use Overtrue\Socialite\Contracts\ProviderInterface as SocialiteProviderInterface;
use Overtrue\Socialite\Providers\OpenWeWork;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;

class Application implements ApplicationInterface
{
Expand All @@ -43,6 +48,8 @@ class Application implements ApplicationInterface

protected ?AccessTokenInterface $suiteAccessToken = null;

protected ?AuthorizerAccessToken $authorizerAccessToken = null;

public function getAccount(): AccountInterface
{
if (! $this->account) {
Expand Down Expand Up @@ -247,21 +254,14 @@ public function getAuthorizerAccessToken(
?AccessTokenInterface $suiteAccessToken = null
): AuthorizerAccessToken {
$suiteAccessToken = $suiteAccessToken ?? $this->getSuiteAccessToken();
$response = $this->getHttpClient()->request('POST', 'cgi-bin/service/get_corp_token', [
'query' => [
'suite_access_token' => $suiteAccessToken->getToken(),
],
'json' => [
'auth_corpid' => $corpId,
'permanent_code' => $permanentCode,
],
])->toArray(false);

if (empty($response['access_token'])) {
throw new HttpException('Failed to get access_token: '.json_encode($response, JSON_UNESCAPED_UNICODE));
}

return new AuthorizerAccessToken($corpId, accessToken: $response['access_token']);
return new AuthorizerAccessToken(
corpId: $corpId,
permanentCodeOrAccessToken: $permanentCode,
suiteAccessToken: $suiteAccessToken,
cache: $this->getCache(),
httpClient: $this->getHttpClient(),
);
}

public function createClient(): AccessTokenAwareClient
Expand All @@ -274,6 +274,41 @@ public function createClient(): AccessTokenAwareClient
))->setPresets($this->config->all());
}

/**
* @throws HttpException
* @throws TransportExceptionInterface
* @throws ServerExceptionInterface
* @throws RedirectionExceptionInterface
* @throws DecodingExceptionInterface
* @throws ClientExceptionInterface
*/
public function getAuthorizerClient(string $corpId, string $permanentCode, ?AccessTokenInterface $suiteAccessToken = null): AccessTokenAwareClient
{
return (new AccessTokenAwareClient(
client: $this->getHttpClient(),
accessToken: $this->getAuthorizerAccessToken($corpId, $permanentCode, $suiteAccessToken),
failureJudge: fn (Response $response) => (bool) ($response->toArray()['errcode'] ?? 0),
throw: (bool) $this->config->get('http.throw', true),
))->setPresets($this->config->all());
}

/**
* @throws HttpException
* @throws TransportExceptionInterface
* @throws ServerExceptionInterface
* @throws RedirectionExceptionInterface
* @throws DecodingExceptionInterface
* @throws ClientExceptionInterface
*/
public function getJsApiTicket(string $corpId, string $permanentCode, ?AccessTokenInterface $suiteAccessToken = null): JsApiTicket
{
return new JsApiTicket(
corpId: $corpId,
cache: $this->getCache(),
httpClient: $this->getAuthorizerClient($corpId, $permanentCode, $suiteAccessToken),
);
}

public function getOAuth(
string $suiteId,
?AccessTokenInterface $suiteAccessToken = null
Expand Down
121 changes: 113 additions & 8 deletions src/OpenWork/AuthorizerAccessToken.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,144 @@

namespace EasyWeChat\OpenWork;

use EasyWeChat\Kernel\Contracts\AccessToken;
use EasyWeChat\Kernel\Contracts\AccessToken as AccessTokenInterface;
use EasyWeChat\Kernel\Contracts\RefreshableAccessToken;
use EasyWeChat\Kernel\Exceptions\HttpException;
use JetBrains\PhpStorm\ArrayShape;
use JetBrains\PhpStorm\Pure;
use Psr\SimpleCache\CacheInterface;
use Psr\SimpleCache\InvalidArgumentException;
use Stringable;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Component\Cache\Psr16Cache;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;

class AuthorizerAccessToken implements AccessToken, Stringable
class AuthorizerAccessToken implements RefreshableAccessToken, Stringable
{
public function __construct(protected string $corpId, protected string $accessToken)
{
protected HttpClientInterface $httpClient;

protected CacheInterface $cache;

public function __construct(
protected string $corpId,
protected string $permanentCodeOrAccessToken,
protected ?AccessTokenInterface $suiteAccessToken = null,
protected ?string $key = null,
?CacheInterface $cache = null,
?HttpClientInterface $httpClient = null,
) {
$this->httpClient = $httpClient ?? HttpClient::create(['base_uri' => 'https://qyapi.weixin.qq.com/']);
$this->cache = $cache ?? new Psr16Cache(new FilesystemAdapter(namespace: 'easywechat', defaultLifetime: 1500));
}

public function getCorpId(): string
{
return $this->corpId;
}

/**
* @throws RedirectionExceptionInterface
* @throws DecodingExceptionInterface
* @throws InvalidArgumentException
* @throws ClientExceptionInterface
* @throws HttpException
* @throws TransportExceptionInterface
* @throws ServerExceptionInterface
*/
public function getToken(): string
{
return $this->accessToken;
if (! isset($this->suiteAccessToken)) {
return $this->permanentCodeOrAccessToken;
}

$token = $this->cache->get($this->getKey());

if ((bool) $token && is_string($token)) {
return $token;
}

return $this->refresh();
}

/**
* @throws RedirectionExceptionInterface
* @throws DecodingExceptionInterface
* @throws InvalidArgumentException
* @throws ClientExceptionInterface
* @throws HttpException
* @throws TransportExceptionInterface
* @throws ServerExceptionInterface
*/
public function __toString()
{
return $this->accessToken;
return $this->getToken();
}

/**
* @return array<string, string>
* @throws RedirectionExceptionInterface
* @throws DecodingExceptionInterface
* @throws InvalidArgumentException
* @throws ClientExceptionInterface
* @throws HttpException
* @throws TransportExceptionInterface
* @throws ServerExceptionInterface
*/
#[ArrayShape(['access_token' => 'string'])]
#[Pure]
#[ArrayShape(['access_token' => 'string'])]
public function toQuery(): array
{
return ['access_token' => $this->getToken()];
}

public function getKey(): string
{
return $this->key ?? $this->key = sprintf('open_work.authorizer.access_token.%s.%s', $this->corpId, $this->permanentCodeOrAccessToken);
}

public function setKey(string $key): static
{
$this->key = $key;

return $this;
}

/**
* @throws HttpException
* @throws TransportExceptionInterface
* @throws ServerExceptionInterface
* @throws RedirectionExceptionInterface
* @throws DecodingExceptionInterface
* @throws ClientExceptionInterface
* @throws InvalidArgumentException
*/
public function refresh(): string
{
if (! isset($this->suiteAccessToken)) {
return '';
}

$response = $this->httpClient->request('POST', 'cgi-bin/service/get_corp_token', [
'query' => [
'suite_access_token' => $this->suiteAccessToken->getToken(),
],
'json' => [
'auth_corpid' => $this->corpId,
'permanent_code' => $this->permanentCodeOrAccessToken,
],
])->toArray(false);

if (empty($response['access_token'])) {
throw new HttpException('Failed to get access_token: '.json_encode($response, JSON_UNESCAPED_UNICODE));
}

$this->cache->set($this->getKey(), $response['access_token'], intval($response['expires_in']));

return $response['access_token'];
}
}
Loading