From 020612357a669fec8f49cb302dac27d9f33f30ed Mon Sep 17 00:00:00 2001
From: XueSi <1592328848@qq.com>
Date: Thu, 23 Sep 2021 21:10:36 +0800
Subject: [PATCH] fix:the issue #148,#147,#161
---
src/OpenPlatform/Application.php | 22 ++-
src/Work/Server/Guard.php | 156 ++++++++++++++++-
src/Work/Server/Handlers/EchoStrHandler.php | 28 ++-
src/Work/Server/ServiceProvider.php | 2 +-
tests/Mock/Message/ServerRequest.php | 13 +-
tests/OpenPlatform/ApplicationTest.php | 23 +++
tests/OpenPlatform/Util/MockCache.php | 145 ++++++++++++++++
tests/OpenPlatform/Util/MockLogger.php | 145 ++++++++++++++++
tests/Work/Server/GuardTest.php | 164 ++++++++----------
.../Server/Handlers/EchoStrHandlerTest.php | 54 ++++--
.../Server/Handlers/mock_data/testHandle.json | 9 +
.../Server/mock_data/clear_mode_request.json | 9 -
.../mock_data/echostr_validate_request.json | 9 +
.../Server/mock_data/message_request.json | 8 +
.../Server/mock_data/safe_mode_request.json | 12 --
15 files changed, 649 insertions(+), 150 deletions(-)
create mode 100644 tests/OpenPlatform/Util/MockCache.php
create mode 100644 tests/OpenPlatform/Util/MockLogger.php
create mode 100644 tests/Work/Server/Handlers/mock_data/testHandle.json
delete mode 100644 tests/Work/Server/mock_data/clear_mode_request.json
create mode 100644 tests/Work/Server/mock_data/echostr_validate_request.json
create mode 100644 tests/Work/Server/mock_data/message_request.json
delete mode 100644 tests/Work/Server/mock_data/safe_mode_request.json
diff --git a/src/OpenPlatform/Application.php b/src/OpenPlatform/Application.php
index 3c880cc..c1f99b0 100644
--- a/src/OpenPlatform/Application.php
+++ b/src/OpenPlatform/Application.php
@@ -64,7 +64,7 @@ class Application extends ServiceContainer
* @param AccessToken|null $accessToken
* @return OfficialAccount
*/
- public function officialAccount(string $appId, string $refreshToken = null, AccessToken $accessToken = null, $reUseLoggerCache = false): OfficialAccount
+ public function officialAccount(string $appId, string $refreshToken = null, AccessToken $accessToken = null): OfficialAccount
{
$officialAccount = new OfficialAccount($this->getAuthorizerConfig($appId, $refreshToken), null, [
ServiceProviders::AccessToken => $accessToken ?: function ($app) {
@@ -84,11 +84,10 @@ public function officialAccount(string $appId, string $refreshToken = null, Acce
}
]);
- if ($reUseLoggerCache) {
- foreach ([ServiceProviders::Cache, ServiceProviders::Logger] as $reuse) {
- if (isset($this[$reuse])) {
- $officialAccount[$reuse] = $this[$reuse];
- }
+ // 复用开放平台的logger cache为授权公众号提供服务
+ foreach ([ServiceProviders::Cache, ServiceProviders::Logger] as $reuse) {
+ if (isset($this[$reuse])) {
+ $officialAccount[$reuse] = $this[$reuse];
}
}
@@ -104,7 +103,7 @@ public function officialAccount(string $appId, string $refreshToken = null, Acce
* @param AccessToken|null $accessToken
* @return MiniProgram
*/
- public function miniProgram(string $appId, string $refreshToken = null, AccessToken $accessToken = null, $reUseLoggerCache = false): MiniProgram
+ public function miniProgram(string $appId, string $refreshToken = null, AccessToken $accessToken = null): MiniProgram
{
$miniProgram = new MiniProgram($this->getAuthorizerConfig($appId, $refreshToken), null, [
ServiceProviders::AccessToken => $accessToken ?: function ($app) {
@@ -121,11 +120,10 @@ public function miniProgram(string $appId, string $refreshToken = null, AccessTo
}
]);
- if ($reUseLoggerCache) {
- foreach ([ServiceProviders::Cache, ServiceProviders::Logger] as $reuse) {
- if (isset($this[$reuse])) {
- $miniProgram[$reuse] = $this[$reuse];
- }
+ // 复用开放平台的logger cache为授权小程序提供服务
+ foreach ([ServiceProviders::Cache, ServiceProviders::Logger] as $reuse) {
+ if (isset($this[$reuse])) {
+ $miniProgram[$reuse] = $this[$reuse];
}
}
diff --git a/src/Work/Server/Guard.php b/src/Work/Server/Guard.php
index 826bafa..9c644ce 100644
--- a/src/Work/Server/Guard.php
+++ b/src/Work/Server/Guard.php
@@ -2,10 +2,23 @@
namespace EasySwoole\WeChat\Work\Server;
+use EasySwoole\WeChat\Kernel\Contracts\MessageInterface;
use EasySwoole\WeChat\Kernel\Contracts\RequestMessageInterface;
+use EasySwoole\WeChat\Kernel\Exceptions\BadRequestException;
+use EasySwoole\WeChat\Kernel\Exceptions\InvalidArgumentException;
+use EasySwoole\WeChat\Kernel\Exceptions\RuntimeException;
+use EasySwoole\WeChat\Kernel\Messages\News;
+use EasySwoole\WeChat\Kernel\Messages\NewsItem;
+use EasySwoole\WeChat\Kernel\Messages\Raw;
+use EasySwoole\WeChat\Kernel\Messages\Text;
+use EasySwoole\WeChat\Kernel\Psr\Response;
use EasySwoole\WeChat\Kernel\ServerGuard;
+use EasySwoole\WeChat\Kernel\ServiceProviders;
+use EasySwoole\WeChat\Kernel\Utility\XML;
+use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use EasySwoole\WeChat\Kernel\Messages\Message;
+use Throwable;
/**
* Class Guard
@@ -24,6 +37,147 @@ public function isValidateRequest(ServerRequestInterface $request): bool
return isset($request->getQueryParams()['echostr']);
}
+ /**
+ * @param ServerRequestInterface $request
+ * @return bool
+ */
+ public function isSafeMode(ServerRequestInterface $request): bool
+ {
+ return true;
+ }
+
+ /**
+ * @param ServerRequestInterface $request
+ * @return $this
+ * @throws BadRequestException
+ */
+ public function validate(ServerRequestInterface $request): ServerGuard
+ {
+ if (!$this->alwaysValidate && !$this->isSafeMode($request)) {
+ return $this;
+ }
+
+ if (empty($request->getQueryParams()['msg_signature'])
+ || empty($request->getQueryParams()['timestamp'])
+ || empty($request->getQueryParams()['nonce'])
+ ) {
+ throw new BadRequestException('Invalid request params.', 400);
+ }
+
+ if (!$this->isValidateRequest($request)) {
+ $postContent = $request->getBody()->getContents();
+ if (0 === stripos($postContent, '<')) {
+ $postContent = XML::parse($postContent);
+ } else {
+ throw new BadRequestException('Invalid request body.', 400);
+ }
+
+ if (empty($postContent) || !is_array($postContent)) {
+ throw new BadRequestException('No message received.');
+ }
+
+ $makeSignature = $this->signature(
+ $this->getToken(),
+ $request->getQueryParams()['timestamp'] ?? "",
+ $request->getQueryParams()['nonce'] ?? "",
+ $postContent['Encrypt']
+ );
+ } else {
+ // echostr 签名校验
+ $makeSignature = $this->signature(
+ $this->getToken(),
+ $request->getQueryParams()['timestamp'] ?? "",
+ $request->getQueryParams()['nonce'] ?? "",
+ $request->getQueryParams()['echostr']
+ );
+ }
+
+ if (($request->getQueryParams()['msg_signature'] ?? "") !== $makeSignature) {
+ throw new BadRequestException('Invalid request signature.', 400);
+ }
+
+ return $this;
+ }
+
+ /**
+ * @param ServerRequestInterface $request
+ * @return ResponseInterface
+ * @throws Throwable
+ */
+ protected function resolve(ServerRequestInterface $request): ResponseInterface
+ {
+ if ($this->isValidateRequest($request)) {
+ $replyMessage = $this->dispatch(Message::VALIDATE, $request);
+ } else {
+ $requestMessage = $this->parseRequest($request);
+ $replyMessage = $this->dispatch($requestMessage->getType(), $requestMessage);
+ }
+
+ /**
+ * "", []
+ */
+ if ((empty($replyMessage) && ($replyMessage !== 0 && $replyMessage !== "0"))) {
+ return new Response(
+ 200,
+ ['Content-Type' => 'application/text'],
+ ''
+ );
+ }
+
+ if ($replyMessage instanceof Raw) {
+ $replyString = $replyMessage->__toString();
+ } else {
+ if (is_string($replyMessage) || is_numeric($replyMessage)) {
+ $replyMessage = new Text($replyMessage);
+ } elseif (is_array($replyMessage) && reset($replyMessage) instanceof NewsItem) {
+ $replyMessage = new News($replyMessage);
+ }
+
+ if (!$replyMessage instanceof MessageInterface) {
+ throw new InvalidArgumentException(sprintf('Invalid Messages type "%s".', gettype($replyMessage)));
+ }
+
+ /** 除非 Message::VALIDATE 出了问题 否则不会走走这里 */
+ if (!isset($requestMessage)) {
+ throw new RuntimeException("Invalid request message.");
+ }
+ $replyString = $this->buildReply($requestMessage->getFromUserName(), $requestMessage->getToUserName(), $replyMessage);
+ }
+
+ if ($this->isSafeMode($request) && !$this->isValidateRequest($request)) {
+ $encrypted = $this->app[ServiceProviders::Encryptor]->encrypt(
+ $replyString,
+ $this->app[ServiceProviders::Config]->get("aesKey"),
+ $this->app[ServiceProviders::Config]->get("corpId")
+ );
+ $replyString = $this->buildEncryptedReply($encrypted);
+ }
+
+ return new Response(
+ 200,
+ ['Content-Type' => 'application/xml'],
+ $replyString
+ );
+ }
+
+
+ /**
+ * @param array $message
+ * @return string|null
+ */
+ protected function decryptMessage(array $message): ?string
+ {
+ $configs = $this->app->getConfig();
+ $appId = $configs['corpId'];
+ $aesKey = $configs['aesKey'];
+
+ return $this->app[ServiceProviders::Encryptor]->decrypt(
+ $message['Encrypt'],
+ $aesKey,
+ $appId
+ );
+ }
+
/**
* @param array $message
* @return RequestMessageInterface
@@ -63,4 +217,4 @@ public function toXmlArray(): array
}
};
}
-}
\ No newline at end of file
+}
diff --git a/src/Work/Server/Handlers/EchoStrHandler.php b/src/Work/Server/Handlers/EchoStrHandler.php
index fd2a516..01f6899 100644
--- a/src/Work/Server/Handlers/EchoStrHandler.php
+++ b/src/Work/Server/Handlers/EchoStrHandler.php
@@ -3,7 +3,10 @@
namespace EasySwoole\WeChat\Work\Server\Handlers;
use EasySwoole\WeChat\Kernel\Contracts\EventHandlerInterface;
+use EasySwoole\WeChat\Kernel\Encryptor;
use EasySwoole\WeChat\Kernel\Messages\Raw;
+use EasySwoole\WeChat\Kernel\ServiceProviders;
+use Pimple\Container;
use Psr\Http\Message\ServerRequestInterface;
/**
@@ -14,10 +17,31 @@
*/
class EchoStrHandler implements EventHandlerInterface
{
+ /**
+ * @var Container
+ */
+ protected $app;
+
+ /**
+ * EchoStrHandler constructor.
+ * @param Container $app
+ */
+ public function __construct(Container $app)
+ {
+ $this->app = $app;
+ }
+
public function handle($request = null)
{
if ($request instanceof ServerRequestInterface) {
- $str = $request->getQueryParams()['echostr'] ?? null;
+ $echoStr = $request->getQueryParams()['echostr'] ?? null;
+ /** @var Encryptor $encryptor */
+ $encryptor = $this->app[ServiceProviders::Encryptor];
+ $str = $encryptor->decrypt(
+ $echoStr,
+ $this->app[ServiceProviders::Config]->get('aesKey'),
+ $this->app[ServiceProviders::Config]->get('corpId')
+ );
if (!is_null($str)) {
return new Raw($str);
}
@@ -25,4 +49,4 @@ public function handle($request = null)
return null;
}
-}
\ No newline at end of file
+}
diff --git a/src/Work/Server/ServiceProvider.php b/src/Work/Server/ServiceProvider.php
index 8eb815d..98d966c 100644
--- a/src/Work/Server/ServiceProvider.php
+++ b/src/Work/Server/ServiceProvider.php
@@ -30,7 +30,7 @@ public function register(Container $app)
if (!isset($app[Application::Server])) {
$app[Application::Server] = function (ServiceContainer $app) {
$guard = new Guard($app);
- $guard->push(new EchoStrHandler(), Message::VALIDATE);
+ $guard->push(new EchoStrHandler($app), Message::VALIDATE);
return $guard;
};
}
diff --git a/tests/Mock/Message/ServerRequest.php b/tests/Mock/Message/ServerRequest.php
index 0a99fda..0ea6c47 100644
--- a/tests/Mock/Message/ServerRequest.php
+++ b/tests/Mock/Message/ServerRequest.php
@@ -21,8 +21,15 @@ public function __construct($method = 'GET', Uri $uri = null, array $headers = n
$query = [];
foreach (explode("&", $this->getUri()->getQuery()) as $group) {
if (!empty($group)) {
- $group = explode("=", $group);
- $query[$group[0]] = $group[1];
+ $groupArr = explode("=", $group);
+
+ // 针对 value 有 = 符号的做兼容
+ if ($groupArr > 2) {
+ $firstPos = strpos($group, '=');
+ $query[$groupArr[0]] = substr($group, $firstPos + 1);
+ } else {
+ $query[$groupArr[0]] = $groupArr[1];
+ }
}
}
$this->withQueryParams($query);
@@ -146,4 +153,4 @@ public function withoutAttribute($name)
unset($this->attributes[$name]);
return $this;
}
-}
\ No newline at end of file
+}
diff --git a/tests/OpenPlatform/ApplicationTest.php b/tests/OpenPlatform/ApplicationTest.php
index 2dfab72..69c5e5b 100644
--- a/tests/OpenPlatform/ApplicationTest.php
+++ b/tests/OpenPlatform/ApplicationTest.php
@@ -6,6 +6,8 @@
use EasySwoole\WeChat\OpenPlatform\Application;
use EasySwoole\WeChat\OpenPlatform\Authorizer\OfficialAccount\Application as OfficialAccount;
use EasySwoole\WeChat\Tests\Mock\Message\Status;
+use EasySwoole\WeChat\Tests\OpenPlatform\Util\MockCache;
+use EasySwoole\WeChat\Tests\OpenPlatform\Util\MockLogger;
use EasySwoole\WeChat\Tests\TestCase;
use Psr\Http\Message\ServerRequestInterface;
use EasySwoole\WeChat\OpenPlatform\Base;
@@ -209,6 +211,27 @@ public function testDynamicCalls2()
$this->assertSame(json_decode($this->readMockResponseJson('createPreAuthorizationCode.json'), true), $client->createPreAuthorizationCode());
}
+ // 保证开放平台授权的应用和开放平台原本的 logger 和 cache 保持一致
+ public function testLogger()
+ {
+ $app = new Application([
+ 'appId' => 'component-app-id',
+ 'appSecret' => 'component-secret',
+ 'token' => 'component-token',
+ 'aesKey' => 'Qqx2S6jV3mp5prWPg5x3eBmeU1kLayZio4Q9ZxWTbmf']);
+
+ $app->rebind(ServiceProviders::Logger, new MockLogger());
+ $app->rebind(ServiceProviders::Cache, new MockCache());
+
+ $miniProgram = $app->miniProgram('mock_miniProgram_app_id', 'mock_miniProgram_refresh-token');
+ $this->assertSame($app->logger, $miniProgram->logger);
+ $this->assertSame($app->cache, $miniProgram->cache);
+
+ $officialAccount = $app->officialAccount('mock_officialAccount_app_id', 'mock_officialAccount_refresh-token');
+ $this->assertSame($app->logger, $officialAccount->logger);
+ $this->assertSame($app->cache, $officialAccount->cache);
+ }
+
protected function readMockResponseJson(string $file): string
{
return file_get_contents(dirname(__FILE__) . '/Base/mock_data/' . $file);
diff --git a/tests/OpenPlatform/Util/MockCache.php b/tests/OpenPlatform/Util/MockCache.php
new file mode 100644
index 0000000..8188b59
--- /dev/null
+++ b/tests/OpenPlatform/Util/MockCache.php
@@ -0,0 +1,145 @@
+
+ */
+
+namespace EasySwoole\WeChat\Tests\OpenPlatform\Util;
+
+use Psr\SimpleCache\CacheInterface;
+
+class MockCache implements CacheInterface
+{
+ /**
+ * Fetches a value from the cache.
+ *
+ * @param string $key The unique key of this item in the cache.
+ * @param mixed $default Default value to return if the key does not exist.
+ *
+ * @return mixed The value of the item from the cache, or $default in case of cache miss.
+ *
+ * @throws \Psr\SimpleCache\InvalidArgumentException
+ * MUST be thrown if the $key string is not a legal value.
+ */
+ public function get($key, $default = null)
+ {
+ // TODO: Implement get() method.
+ }
+
+ /**
+ * Persists data in the cache, uniquely referenced by a key with an optional expiration TTL time.
+ *
+ * @param string $key The key of the item to store.
+ * @param mixed $value The value of the item to store, must be serializable.
+ * @param null|int|\DateInterval $ttl Optional. The TTL value of this item. If no value is sent and
+ * the driver supports TTL then the library may set a default value
+ * for it or let the driver take care of that.
+ *
+ * @return bool True on success and false on failure.
+ *
+ * @throws \Psr\SimpleCache\InvalidArgumentException
+ * MUST be thrown if the $key string is not a legal value.
+ */
+ public function set($key, $value, $ttl = null)
+ {
+ // TODO: Implement set() method.
+ }
+
+ /**
+ * Delete an item from the cache by its unique key.
+ *
+ * @param string $key The unique cache key of the item to delete.
+ *
+ * @return bool True if the item was successfully removed. False if there was an error.
+ *
+ * @throws \Psr\SimpleCache\InvalidArgumentException
+ * MUST be thrown if the $key string is not a legal value.
+ */
+ public function delete($key)
+ {
+ // TODO: Implement delete() method.
+ }
+
+ /**
+ * Wipes clean the entire cache's keys.
+ *
+ * @return bool True on success and false on failure.
+ */
+ public function clear()
+ {
+ // TODO: Implement clear() method.
+ }
+
+ /**
+ * Obtains multiple cache items by their unique keys.
+ *
+ * @param iterable $keys A list of keys that can obtained in a single operation.
+ * @param mixed $default Default value to return for keys that do not exist.
+ *
+ * @return iterable A list of key => value pairs. Cache keys that do not exist or are stale will have $default as value.
+ *
+ * @throws \Psr\SimpleCache\InvalidArgumentException
+ * MUST be thrown if $keys is neither an array nor a Traversable,
+ * or if any of the $keys are not a legal value.
+ */
+ public function getMultiple($keys, $default = null)
+ {
+ // TODO: Implement getMultiple() method.
+ }
+
+ /**
+ * Persists a set of key => value pairs in the cache, with an optional TTL.
+ *
+ * @param iterable $values A list of key => value pairs for a multiple-set operation.
+ * @param null|int|\DateInterval $ttl Optional. The TTL value of this item. If no value is sent and
+ * the driver supports TTL then the library may set a default value
+ * for it or let the driver take care of that.
+ *
+ * @return bool True on success and false on failure.
+ *
+ * @throws \Psr\SimpleCache\InvalidArgumentException
+ * MUST be thrown if $values is neither an array nor a Traversable,
+ * or if any of the $values are not a legal value.
+ */
+ public function setMultiple($values, $ttl = null)
+ {
+ // TODO: Implement setMultiple() method.
+ }
+
+ /**
+ * Deletes multiple cache items in a single operation.
+ *
+ * @param iterable $keys A list of string-based keys to be deleted.
+ *
+ * @return bool True if the items were successfully removed. False if there was an error.
+ *
+ * @throws \Psr\SimpleCache\InvalidArgumentException
+ * MUST be thrown if $keys is neither an array nor a Traversable,
+ * or if any of the $keys are not a legal value.
+ */
+ public function deleteMultiple($keys)
+ {
+ // TODO: Implement deleteMultiple() method.
+ }
+
+ /**
+ * Determines whether an item is present in the cache.
+ *
+ * NOTE: It is recommended that has() is only to be used for cache warming type purposes
+ * and not to be used within your live applications operations for get/set, as this method
+ * is subject to a race condition where your has() will return true and immediately after,
+ * another script can remove it making the state of your app out of date.
+ *
+ * @param string $key The cache item key.
+ *
+ * @return bool
+ *
+ * @throws \Psr\SimpleCache\InvalidArgumentException
+ * MUST be thrown if the $key string is not a legal value.
+ */
+ public function has($key)
+ {
+ // TODO: Implement has() method.
+ }
+}
diff --git a/tests/OpenPlatform/Util/MockLogger.php b/tests/OpenPlatform/Util/MockLogger.php
new file mode 100644
index 0000000..a55abf3
--- /dev/null
+++ b/tests/OpenPlatform/Util/MockLogger.php
@@ -0,0 +1,145 @@
+
+ */
+
+namespace EasySwoole\WeChat\Tests\OpenPlatform\Util;
+
+use Psr\Log\LoggerInterface;
+
+class MockLogger implements LoggerInterface
+{
+
+ /**
+ * System is unusable.
+ *
+ * @param string $message
+ * @param mixed[] $context
+ *
+ * @return void
+ */
+ public function emergency($message, array $context = array())
+ {
+ // TODO: Implement emergency() method.
+ }
+
+ /**
+ * Action must be taken immediately.
+ *
+ * Example: Entire website down, database unavailable, etc. This should
+ * trigger the SMS alerts and wake you up.
+ *
+ * @param string $message
+ * @param mixed[] $context
+ *
+ * @return void
+ */
+ public function alert($message, array $context = array())
+ {
+ // TODO: Implement alert() method.
+ }
+
+ /**
+ * Critical conditions.
+ *
+ * Example: Application component unavailable, unexpected exception.
+ *
+ * @param string $message
+ * @param mixed[] $context
+ *
+ * @return void
+ */
+ public function critical($message, array $context = array())
+ {
+ // TODO: Implement critical() method.
+ }
+
+ /**
+ * Runtime errors that do not require immediate action but should typically
+ * be logged and monitored.
+ *
+ * @param string $message
+ * @param mixed[] $context
+ *
+ * @return void
+ */
+ public function error($message, array $context = array())
+ {
+ // TODO: Implement error() method.
+ }
+
+ /**
+ * Exceptional occurrences that are not errors.
+ *
+ * Example: Use of deprecated APIs, poor use of an API, undesirable things
+ * that are not necessarily wrong.
+ *
+ * @param string $message
+ * @param mixed[] $context
+ *
+ * @return void
+ */
+ public function warning($message, array $context = array())
+ {
+ // TODO: Implement warning() method.
+ }
+
+ /**
+ * Normal but significant events.
+ *
+ * @param string $message
+ * @param mixed[] $context
+ *
+ * @return void
+ */
+ public function notice($message, array $context = array())
+ {
+ // TODO: Implement notice() method.
+ }
+
+ /**
+ * Interesting events.
+ *
+ * Example: User logs in, SQL logs.
+ *
+ * @param string $message
+ * @param mixed[] $context
+ *
+ * @return void
+ */
+ public function info($message, array $context = array())
+ {
+ // TODO: Implement info() method.
+ }
+
+ /**
+ * Detailed debug information.
+ *
+ * @param string $message
+ * @param mixed[] $context
+ *
+ * @return void
+ */
+ public function debug($message, array $context = array())
+ {
+ // TODO: Implement debug() method.
+ }
+
+ /**
+ * Logs with an arbitrary level.
+ *
+ * @param mixed $level
+ * @param string $message
+ * @param mixed[] $context
+ *
+ * @return void
+ *
+ * @throws \Psr\Log\InvalidArgumentException
+ */
+ public function log($level, $message, array $context = array())
+ {
+ // TODO: Implement log() method.
+ }
+}
diff --git a/tests/Work/Server/GuardTest.php b/tests/Work/Server/GuardTest.php
index cdc8fc2..f681f88 100644
--- a/tests/Work/Server/GuardTest.php
+++ b/tests/Work/Server/GuardTest.php
@@ -9,168 +9,144 @@
namespace EasySwoole\WeChat\Tests\Work\Server;
-
-use EasySwoole\WeChat\Kernel\Contracts\RequestMessageInterface;
+use EasySwoole\WeChat\Kernel\Contracts\MessageInterface;
+use EasySwoole\WeChat\Kernel\Encryptor;
use EasySwoole\WeChat\Kernel\Exceptions\BadRequestException;
+use EasySwoole\WeChat\Kernel\Messages\Message;
+use EasySwoole\WeChat\Kernel\Messages\Text;
use EasySwoole\WeChat\Kernel\ServiceContainer;
-use EasySwoole\WeChat\Kernel\Utility\XML;
+use EasySwoole\WeChat\Kernel\ServiceProviders;
use EasySwoole\WeChat\Tests\Mock\Message\Status;
use EasySwoole\WeChat\Tests\TestCase;
use EasySwoole\WeChat\Work\Server\Guard;
-use EasySwoole\WeChat\Work\Server\ServiceProvider;
+use EasySwoole\WeChat\Work\Server\Handlers\EchoStrHandler;
use Psr\Http\Message\ResponseInterface;
use ReflectionProperty;
class GuardTest extends TestCase
{
/**
- * @throws \EasySwoole\WeChat\Kernel\Exceptions\BadRequestException
+ * 【验证回调URL】
+ * 企业开启回调模式时,企业号会向验证url发送一个get请求
+ * 假设点击验证时,企业收到类似请求:
+ * GET /cgi-bin/wxpush?msg_signature=5c45ff5e21c57e6ad56bac8758b79b1d9ac89fd3×tamp=1409659589&nonce=263014780&echostr=P9nAzCzyDtyTWESHep1vC5X9xho%2FqYX3Zpb4yKa9SKld1DsH3Iyt3tP3zNdtp%2B4RPcs8TgAE7OaBO%2BFZXvnaqQ%3D%3D
+ * HTTP/1.1 Host: qy.weixin.qq.com
+ *
+ * @throws BadRequestException
+ * @throws \EasySwoole\WeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \Throwable
*/
- public function testServer()
+ public function testServerForValidateCallbackUrl()
{
- $mockData = $this->readMockData('clear_mode_request.json');
+ $mockData = $this->readMockData('echostr_validate_request.json');
$mockData = json_decode($mockData, true);
$mockRequest = $this->buildRequest(
- "POST",
- "https://test.com?" . http_build_query($mockData['query']),
+ "GET",
+ "https://qy.weixin.qq.com/cgi-bin/wxpush?" . urldecode(http_build_query($mockData['query'])),
[],
$mockData['body']
);
$app = new ServiceContainer([
- 'corpId' => 'mock_corpId',
+ 'corpId' => 'wx5823bf96d3bd56c7',
'corpSecret' => 'mock_corpSecret',
- 'token' => 'mock_token'
+ 'token' => 'QDG6eK',
+ 'aesKey' => 'jWmYm7qr5nMoAUwZRjGtBxmz3KA1tkAj3ykkR6q2B2C',
]);
+
+ $app->rebind(ServiceProviders::Encryptor, new Encryptor());
+
$guard = new Guard($app);
+ $guard->push(new EchoStrHandler($app), Message::VALIDATE);
+
$response = $guard->serve($mockRequest);
$this->assertInstanceOf(ResponseInterface::class, $response);
$this->assertSame(Status::CODE_OK, $response->getStatusCode());
- $this->assertSame(Guard::SUCCESS_EMPTY_RESPONSE, $response->getBody()->__toString());
- $this->assertSame(['Content-Type' => ['application/text']], $response->getHeaders());
+ $this->assertSame('1616140317555161061', $response->getBody()->__toString());
+ $this->assertSame(['Content-Type' => ['application/xml']], $response->getHeaders());
}
/**
- * @throws \ReflectionException
+ * 【对用户回复的消息解密、企业回复用户消息的加密】
+ *
+ * @throws BadRequestException
+ * @throws \EasySwoole\WeChat\Kernel\Exceptions\InvalidArgumentException
+ * @throws \Throwable
*/
- public function testForceValidate()
- {
- $app = new ServiceContainer([
- 'corpId' => 'mock_corpId',
- 'corpSecret' => 'mock_corpSecret',
- 'token' => 'mock_token'
- ]);
- $guard = new Guard($app);
-
- $reflectionProperty = new ReflectionProperty($guard, 'alwaysValidate');
- $reflectionProperty->setAccessible(true);
- $this->assertFalse($reflectionProperty->getValue($guard));
-
- $guard->forceValidate();
- $this->assertTrue($reflectionProperty->getValue($guard));
- }
-
- public function testIsSafeMode()
+ public function testServerForMessageRequest()
{
+ $mockData = $this->readMockData('message_request.json');
+ $mockData = json_decode($mockData, true);
$mockRequest = $this->buildRequest(
- "POST",
- "https://test.com?signature=xxx&encrypt_type=aes"
+ "GET",
+ "https://qy.weixin.qq.com/cgi-bin/wxpush?" . urldecode(http_build_query($mockData['query'])),
+ [],
+ $mockData['body']
);
$app = new ServiceContainer([
- 'corpId' => 'mock_corpId',
+ 'corpId' => 'wx5823bf96d3bd56c7',
'corpSecret' => 'mock_corpSecret',
- 'token' => 'mock_token'
+ 'token' => 'QDG6eK',
+ 'aesKey' => 'jWmYm7qr5nMoAUwZRjGtBxmz3KA1tkAj3ykkR6q2B2C',
]);
- $guard = new Guard($app);
- $this->assertTrue($guard->isSafeMode($mockRequest));
- $mockRequest = $this->buildRequest(
- "POST",
- "https://test.com?signature=xxx"
- );
- $this->assertFalse($guard->isSafeMode($mockRequest));
- }
-
- public function testIsValidateRequest()
- {
- $mockRequest = $this->buildRequest(
- "POST",
- "https://test.com?echostr=mock-echostr"
- );
+ $app->rebind(ServiceProviders::Encryptor, new Encryptor());
- $app = new ServiceContainer([
- 'corpId' => 'mock_corpId',
- 'corpSecret' => 'mock_corpSecret',
- 'token' => 'mock_token'
- ]);
$guard = new Guard($app);
- $this->assertTrue($guard->isValidateRequest($mockRequest));
- $mockRequest = $this->buildRequest(
- "POST",
- "https://test.com?signature=xxx"
- );
- $this->assertFalse($guard->isValidateRequest($mockRequest));
+ $guard->push(function (MessageInterface $message) {
+ return new Text('Hello EasySwooleWeChat!');
+ }, Message::TEXT);
+
+ $response = $guard->serve($mockRequest);
+
+ $this->assertInstanceOf(ResponseInterface::class, $response);
+ $this->assertSame(Status::CODE_OK, $response->getStatusCode());
+ $this->assertIsString($response->getBody()->__toString());
+ $this->assertSame(['Content-Type' => ['application/xml']], $response->getHeaders());
}
/**
- * @throws BadRequestException
+ * @throws \ReflectionException
*/
- public function testParseRequest()
+ public function testForceValidate()
{
- $mockData = $this->readMockData('clear_mode_request.json');
- $mockData = json_decode($mockData, true);
- $mockRequest = $this->buildRequest(
- "POST",
- "https://test.com?" . http_build_query($mockData['query']),
- [],
- $mockData['body']
- );
-
$app = new ServiceContainer([
'corpId' => 'mock_corpId',
'corpSecret' => 'mock_corpSecret',
- 'token' => 'dczmnau31ea9nzcn13i91uedjdal'
+ 'token' => 'mock_token'
]);
+
$guard = new Guard($app);
- $this->assertInstanceOf(RequestMessageInterface::class, $guard->parseRequest($mockRequest));
+ $reflectionProperty = new ReflectionProperty($guard, 'alwaysValidate');
+ $reflectionProperty->setAccessible(true);
+
+ $this->assertFalse($reflectionProperty->getValue($guard));
+
+ $guard->forceValidate();
- $this->assertEquals($mockData['body'], $guard->parseRequest($mockRequest)->transformToXml());
- $this->assertEquals(XML::parse($mockData['body']), $guard->parseRequest($mockRequest)->transformForJsonRequest());
+ $this->assertTrue($reflectionProperty->getValue($guard));
}
- /**
- * @throws BadRequestException
- */
- public function testParseRequestWithSafeMode()
+ public function testIsSafeMode()
{
- $mockData = $this->readMockData('safe_mode_request.json');
- $mockData = json_decode($mockData, true);
$mockRequest = $this->buildRequest(
"POST",
- "https://test.com?" . http_build_query($mockData['query']),
- [],
- $mockData['body']
+ "https://qy.weixin.qq.com?msg_signature=ASDFQWEXZCVAQFASDFASDFSS×tamp=13500001234&nonce=123412323"
);
$app = new ServiceContainer([
'corpId' => 'mock_corpId',
'corpSecret' => 'mock_corpSecret',
- 'token' => 'dczmnau31ea9nzcn13i91uedjdal',
- 'aesKey' => 'pz2Zl2by0oZQ6AUV4EvnbF7A7VRUvrLN1vqnxsUjmTW'
+ 'token' => 'mock_token'
]);
- $app->register(new ServiceProvider());
$guard = new Guard($app);
- $this->assertInstanceOf(RequestMessageInterface::class, $guard->parseRequest($mockRequest));
-
- $this->assertEquals($mockData['clear_body'], $guard->parseRequest($mockRequest)->transformToXml());
- $this->assertEquals(XML::parse($mockData['clear_body']), $guard->parseRequest($mockRequest)->transformForJsonRequest());
+ $this->assertTrue($guard->isSafeMode($mockRequest));
}
/**
@@ -181,4 +157,4 @@ private function readMockData(string $filename): string
{
return file_get_contents(__DIR__ . '/mock_data/' . $filename);
}
-}
\ No newline at end of file
+}
diff --git a/tests/Work/Server/Handlers/EchoStrHandlerTest.php b/tests/Work/Server/Handlers/EchoStrHandlerTest.php
index 6b5cb5d..4bcbcb7 100644
--- a/tests/Work/Server/Handlers/EchoStrHandlerTest.php
+++ b/tests/Work/Server/Handlers/EchoStrHandlerTest.php
@@ -10,7 +10,10 @@
namespace EasySwoole\WeChat\Tests\Work\Server\Handlers;
+use EasySwoole\WeChat\Kernel\Encryptor;
use EasySwoole\WeChat\Kernel\Messages\Raw;
+use EasySwoole\WeChat\Kernel\ServiceContainer;
+use EasySwoole\WeChat\Kernel\ServiceProviders;
use EasySwoole\WeChat\Tests\Mock\Message\Request;
use EasySwoole\WeChat\Tests\TestCase;
use EasySwoole\WeChat\Work\Server\Handlers\EchoStrHandler;
@@ -19,34 +22,53 @@ class EchoStrHandlerTest extends TestCase
{
public function testHandle()
{
+ $mockData = $this->readMockData('testHandle.json');
+ $mockData = json_decode($mockData, true);
$mockRequest = $this->buildRequest(
- "POST",
- "https://test.com?echostr=mock-echostr"
+ "GET",
+ "https://qy.weixin.qq.com/cgi-bin/wxpush?" . urldecode(http_build_query($mockData['query'])),
+ [],
+ $mockData['body']
);
- $handle = new EchoStrHandler();
+ $app = new ServiceContainer([
+ 'corpId' => 'wx5823bf96d3bd56c7',
+ 'corpSecret' => 'mock_corpSecret',
+ 'token' => 'QDG6eK',
+ 'aesKey' => 'jWmYm7qr5nMoAUwZRjGtBxmz3KA1tkAj3ykkR6q2B2C',
+ ]);
+
+ $app->rebind(ServiceProviders::Encryptor, new Encryptor());
+
+ $handle = new EchoStrHandler($app);
$result = $handle->handle($mockRequest);
$this->assertInstanceOf(Raw::class, $result);
- $this->assertSame("mock-echostr", $result->getContent());
+ $this->assertSame("1616140317555161061", $result->getContent());
}
- public function testHandleWithoutEchoStr()
+ public function testHandleInvalidRequest()
{
- $mockRequest = $this->buildRequest(
- "POST",
- "https://test.com?signature=xxx"
- );
+ $app = new ServiceContainer([
+ 'corpId' => 'wx5823bf96d3bd56c7',
+ 'corpSecret' => 'mock_corpSecret',
+ 'token' => 'QDG6eK',
+ 'aesKey' => 'jWmYm7qr5nMoAUwZRjGtBxmz3KA1tkAj3ykkR6q2B2C',
+ ]);
- $handle = new EchoStrHandler();
+ $app->rebind(ServiceProviders::Encryptor, new Encryptor());
- $this->assertSame(null, $handle->handle($mockRequest));
+ $handle = new EchoStrHandler($app);
+
+ $this->assertSame(null, $handle->handle(new Request()));
}
- public function testHandleInvalidRequest()
+ /**
+ * @param string $filename
+ * @return string
+ */
+ private function readMockData(string $filename): string
{
- $handle = new EchoStrHandler();
-
- $this->assertSame(null, $handle->handle(new Request()));
+ return file_get_contents(__DIR__ . '/mock_data/' . $filename);
}
-}
\ No newline at end of file
+}
diff --git a/tests/Work/Server/Handlers/mock_data/testHandle.json b/tests/Work/Server/Handlers/mock_data/testHandle.json
new file mode 100644
index 0000000..8fc3eb1
--- /dev/null
+++ b/tests/Work/Server/Handlers/mock_data/testHandle.json
@@ -0,0 +1,9 @@
+{
+ "query": {
+ "msg_signature": "5c45ff5e21c57e6ad56bac8758b79b1d9ac89fd3",
+ "timestamp": "1409659589",
+ "nonce": "263014780",
+ "echostr": "P9nAzCzyDtyTWESHep1vC5X9xho/qYX3Zpb4yKa9SKld1DsH3Iyt3tP3zNdtp+4RPcs8TgAE7OaBO+FZXvnaqQ=="
+ },
+ "body": ""
+}
diff --git a/tests/Work/Server/mock_data/clear_mode_request.json b/tests/Work/Server/mock_data/clear_mode_request.json
deleted file mode 100644
index d737224..0000000
--- a/tests/Work/Server/mock_data/clear_mode_request.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "query": {
- "signature": "34a079a3581b6099afebf59cfc1f570ee488f9d0",
- "timestamp": "1606128379",
- "nonce": "1095164962",
- "openid": "o1YYd0mJxkRO0eCi7haWLDVMalts"
- },
- "body": "160612837922994230328393770"
-}
\ No newline at end of file
diff --git a/tests/Work/Server/mock_data/echostr_validate_request.json b/tests/Work/Server/mock_data/echostr_validate_request.json
new file mode 100644
index 0000000..8fc3eb1
--- /dev/null
+++ b/tests/Work/Server/mock_data/echostr_validate_request.json
@@ -0,0 +1,9 @@
+{
+ "query": {
+ "msg_signature": "5c45ff5e21c57e6ad56bac8758b79b1d9ac89fd3",
+ "timestamp": "1409659589",
+ "nonce": "263014780",
+ "echostr": "P9nAzCzyDtyTWESHep1vC5X9xho/qYX3Zpb4yKa9SKld1DsH3Iyt3tP3zNdtp+4RPcs8TgAE7OaBO+FZXvnaqQ=="
+ },
+ "body": ""
+}
diff --git a/tests/Work/Server/mock_data/message_request.json b/tests/Work/Server/mock_data/message_request.json
new file mode 100644
index 0000000..ee37f12
--- /dev/null
+++ b/tests/Work/Server/mock_data/message_request.json
@@ -0,0 +1,8 @@
+{
+ "query": {
+ "msg_signature": "477715d11cdb4164915debcba66cb864d751f3e6",
+ "timestamp": "1409659813",
+ "nonce": "1372623149"
+ },
+ "body": ""
+}
diff --git a/tests/Work/Server/mock_data/safe_mode_request.json b/tests/Work/Server/mock_data/safe_mode_request.json
deleted file mode 100644
index 0ce7b22..0000000
--- a/tests/Work/Server/mock_data/safe_mode_request.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "query": {
- "signature":"35c35a17908ebfaf8540ee11f7590764a88df32e",
- "timestamp":"1606180982",
- "nonce":"535086195",
- "openid":"o1YYd0mJxkRO0eCi7haWLDVMalts",
- "encrypt_type":"aes",
- "msg_signature":"4f972a15c29b10ead13819cf575ed6154cb44715"
- },
- "body": "",
- "clear_body": "160618098222994982940022977"
-}
\ No newline at end of file