This is an automated email from the ASF dual-hosted git repository.
xiaoyu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new b1b857a4b [ISSUE #3628] Signature plugin extension (#3637)
b1b857a4b is described below
commit b1b857a4b88348ac39b09d249254a29f8f80aa9c
Author: 云扬四海 <[email protected]>
AuthorDate: Mon Jul 4 18:48:03 2022 +0800
[ISSUE #3628] Signature plugin extension (#3637)
* [ISSUE #3628] Signature plugin extension
* [ISSUE #3628] Signature plugin extension
* [ISSUE #3628] Signature plugin extension
* [ISSUE #3628] Signature plugin extension
---
db/init/mysql/schema.sql | 3 +
db/init/oracle/schema.sql | 9 ++
db/init/pg/create-table.sql | 4 +-
.../src/main/resources/sql-script/h2/schema.sql | 3 +
.../org/apache/shenyu/common/utils/JsonUtils.java | 16 +++
.../apache/shenyu/common/utils/JsonUtilsTest.java | 6 +
.../test/http/combination/SignPluginTest.java | 124 +++++++++++++++++++--
.../org/apache/shenyu/plugin/sign/SignPlugin.java | 65 ++++++++++-
.../apache/shenyu/plugin/sign/api/SignService.java | 14 ++-
.../sign/decorator/SignRequestDecorator.java | 56 ++++++++++
.../plugin/sign/handler/SignPluginDataHandler.java | 55 +++++++++
.../SignRuleHandler.java} | 38 ++++---
.../plugin/sign/service/DefaultSignService.java | 21 ++--
.../sign/subscriber/SignAuthDataSubscriber.java | 2 +-
.../apache/shenyu/plugin/sign/SignPluginTest.java | 104 ++++++++++++++++-
.../sign/service/DefaultSignServiceTest.java | 35 ++++++
.../subscriber/SignAuthDataSubscriberTest.java | 63 +++++++++++
.../plugin/sign/SignPluginConfiguration.java | 12 ++
18 files changed, 592 insertions(+), 38 deletions(-)
diff --git a/db/init/mysql/schema.sql b/db/init/mysql/schema.sql
index 50cced086..0c7df883b 100644
--- a/db/init/mysql/schema.sql
+++ b/db/init/mysql/schema.sql
@@ -788,6 +788,7 @@ INSERT INTO `plugin_handle` VALUES ('1529402613204172880',
'32', 'maxRequestBody
INSERT INTO `plugin_handle` VALUES ('1529402613204172881', '32',
'compressAlg', 'compressAlg', 3, 3, 6,
'{\"required\":\"0\",\"defaultValue\":\"none\"}', '2022-06-19 22:00:00',
'2022-06-19 22:00:00');
INSERT INTO `plugin_handle` VALUES ('1529402613204172882', '32', 'index',
'index', 2, 1, 1,
'{\"required\":\"0\",\"defaultValue\":\"\",\"placeholder\":\"optional\"}',
'2022-06-19 22:00:00', '2022-06-19 22:00:00');
INSERT INTO `plugin_handle` VALUES ('1529402613204172883', '32', 'sampleRate',
'sampleRate', 2, 1, 2,
'{\"required\":\"0\",\"defaultValue\":\"\",\"placeholder\":\"optional,0,0.01~1\"}',
'2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO `plugin_handle` VALUES ('1529402613204172884', '1',
'signRequestBody', 'signRequestBody', 3, 2, 9,
'{"required":"0","defaultValue":"false","placeholder":"signRequestBody","rule":""}',
'2022-06-29 10:08:02', '2022-06-29 10:08:02');
-- ----------------------------
@@ -1361,6 +1362,8 @@ INSERT INTO `shenyu_dict` VALUES ('1529402613195784243',
'compressAlg', 'COMPRES
INSERT INTO `shenyu_dict` VALUES ('1529402613195784244', 'cacheType',
'CACHE_TYPE_MEMORY', 'memory', 'memory', 'use memory to cache data', 0, 1,
'2022-05-25 18:02:53', '2022-05-25 18:02:53');
INSERT INTO `shenyu_dict` VALUES ('1529402613195784245', 'cacheType',
'CACHE_TYPE_REDIS', 'redis', 'redis', 'use redis to cache data', 1, 1,
'2022-05-25 18:02:53', '2022-05-25 18:02:53');
INSERT INTO `shenyu_dict` VALUES ('1529402613195784246', 'threadpool',
'THREADPOOL', 'default', 'default', '', 5, 1, '2022-05-25 18:02:53',
'2022-05-25 18:02:53');
+INSERT INTO `shenyu_dict` VALUES ('1529402613195784247', 'signRequestBody',
'SIGN_REQUEST_BODY', 'close', 'false', 'close', 1, 1, '2022-06-29 10:08:02',
'2022-06-29 10:08:02');
+INSERT INTO `shenyu_dict` VALUES ('1529402613195784248', 'signRequestBody',
'SIGN_REQUEST_BODY', 'open', 'true', 'open', 0, 1, '2022-06-29 10:08:02',
'2022-06-29 10:08:02');
-- ----------------------------
-- Table structure for user_role
diff --git a/db/init/oracle/schema.sql b/db/init/oracle/schema.sql
index 31d755a4c..028ae69ad 100644
--- a/db/init/oracle/schema.sql
+++ b/db/init/oracle/schema.sql
@@ -802,6 +802,12 @@ values ('1518229897206079531', 'cacheType',
'CACHE_TYPE_REDIS', 'redis', 'redis'
insert /*+ IGNORE_ROW_ON_DUPKEY_INDEX(shenyu_dict(type, dict_code, dict_name))
*/ into SHENYU_DICT (ID, TYPE, DICT_CODE, DICT_NAME, DICT_VALUE, "desc", SORT,
ENABLED)
values ('1518229897206079532', 'threadpool', 'THREADPOOL', 'default',
'default', null, 5, 1);
+insert /*+ IGNORE_ROW_ON_DUPKEY_INDEX(shenyu_dict(type, dict_code, dict_name))
*/ into SHENYU_DICT (ID, TYPE, DICT_CODE, DICT_NAME, DICT_VALUE, "desc", SORT,
ENABLED)
+values ('1518229897206079533', 'signRequestBody', 'SIGN_REQUEST_BODY',
'close', 'false', 'close', 1, 1);
+
+insert /*+ IGNORE_ROW_ON_DUPKEY_INDEX(shenyu_dict(type, dict_code, dict_name))
*/ into SHENYU_DICT (ID, TYPE, DICT_CODE, DICT_NAME, DICT_VALUE, "desc", SORT,
ENABLED)
+values ('1518229897206079534', 'signRequestBody', 'SIGN_REQUEST_BODY', 'open',
'true', 'open', 0, 1);
+
/*plugin*/
INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(plugin(id)) */ INTO plugin (id, name,
role, sort, enabled) VALUES ('1','sign','Authentication', 20, '0');
INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(plugin(id)) */ INTO plugin (id, name,
role, sort,config,enabled) VALUES ('2','waf', 'Authentication',
50,'{"model":"black"}','0');
@@ -1340,6 +1346,9 @@ values ('1518229897214468174', '32', 'index', 'index', 2,
1, 1, '{"required":"0"
insert /*+ IGNORE_ROW_ON_DUPKEY_INDEX(plugin_handle(plugin_id, field, type))
*/ into plugin_handle (ID, PLUGIN_ID, FIELD, LABEL, DATA_TYPE, TYPE, SORT,
EXT_OBJ)
values ('1518229897214468175', '32', 'sampleRate', 'sampleRate', 2, 1, 2,
'{"required":"0","defaultValue":"","placeholder":"optional,0,0.01~1"}');
+insert /*+ IGNORE_ROW_ON_DUPKEY_INDEX(plugin_handle(plugin_id, field, type))
*/ into plugin_handle (ID, PLUGIN_ID, FIELD, LABEL, DATA_TYPE, TYPE, SORT,
EXT_OBJ)
+values ('1518229897214468176', '1', 'signRequestBody', 'signRequestBody', 3,
2, 9,
'{"required":"0","defaultValue":"false","placeholder":"signRequestBody","rule":""}');
+
/** insert resource for resource */
diff --git a/db/init/pg/create-table.sql b/db/init/pg/create-table.sql
index d24fa2c73..c59a382b6 100644
--- a/db/init/pg/create-table.sql
+++ b/db/init/pg/create-table.sql
@@ -873,6 +873,7 @@ INSERT INTO "public"."plugin_handle" VALUES
('1529403902783524925', '32', 'maxRe
INSERT INTO "public"."plugin_handle" VALUES ('1529403902783524926', '32',
'compressAlg', 'compressAlg', 3, 3, 7,
'{"required":"0","defaultValue":"none"}', '2022-06-19 22:00:00', '2022-06-19
22:00:00');
INSERT INTO "public"."plugin_handle" VALUES ('1529403902783524927', '32',
'index', 'index', 2, 1, 1,
'{"required":"0","defaultValue":"","placeholder":"optional"}', '2022-06-19
22:00:00', '2022-06-19 22:00:00');
INSERT INTO "public"."plugin_handle" VALUES ('1529403902783524928', '32',
'sampleRate', 'sampleRate', 2, 1, 2,
'{"required":"0","defaultValue":"","placeholder":"optional,0,0.01~1"}',
'2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO "public"."plugin_handle" VALUES ('1529403902783524929', '1',
'signRequestBody', 'signRequestBody', 3, 2, 9,
'{"required":"0","defaultValue":"false","placeholder":"signRequestBody","rule":""}',
'2022-06-29 10:08:02', '2022-06-29 10:08:02');
-- ----------------------------
-- Table structure for resource
@@ -1514,7 +1515,8 @@ INSERT INTO "public"."shenyu_dict" VALUES
('1529403902800302096', 'cacheType', '
INSERT INTO "public"."shenyu_dict" VALUES ('1529403902800302097', 'cacheType',
'CACHE_TYPE_REDIS', 'redis', 'redis', 'use redis to cache data', 1, 1,
'2022-05-25 18:08:02', '2022-05-25 18:08:02');
INSERT INTO "public"."shenyu_dict" VALUES ('1529403902800302093', 'table',
'INIT_FLAG', 'status', 'true', 'table(resource,permission) init status', 0, 0,
'2022-05-25 18:08:02', '2022-05-25 18:08:07.275');
INSERT INTO "public"."shenyu_dict" VALUES ('1529403902800302098',
'threadpool', 'THREADPOOL', 'default', 'default', '', 5, 1, '2022-05-25
18:08:02', '2022-05-25 18:08:02');
-
+INSERT INTO "public"."shenyu_dict" VALUES ('1529403902800302099',
'signRequestBody', 'SIGN_REQUEST_BODY', 'close', 'false', 'close', 1, 1,
'2022-06-29 10:08:02', '2022-06-29 10:08:02');
+INSERT INTO "public"."shenyu_dict" VALUES ('1529403902800302100',
'signRequestBody', 'SIGN_REQUEST_BODY', 'open', 'true', 'open', 0, 1,
'2022-06-29 10:08:02', '2022-06-29 10:08:02');
-- ----------------------------
-- Table structure for user_role
diff --git a/shenyu-admin/src/main/resources/sql-script/h2/schema.sql
b/shenyu-admin/src/main/resources/sql-script/h2/schema.sql
index 6b617a91d..305f71bae 100644
--- a/shenyu-admin/src/main/resources/sql-script/h2/schema.sql
+++ b/shenyu-admin/src/main/resources/sql-script/h2/schema.sql
@@ -348,6 +348,8 @@ INSERT IGNORE INTO `shenyu_dict` (`id`, `type`,`dict_code`,
`dict_name`, `dict_v
INSERT IGNORE INTO `shenyu_dict` (`id`, `type`,`dict_code`, `dict_name`,
`dict_value`, `desc`, `sort`, `enabled`) VALUES ('1529402613195784244',
'cacheType', 'CACHE_TYPE_MEMORY', 'memory', 'memory', 'use memory to cache
data', 0, 1);
INSERT IGNORE INTO `shenyu_dict` (`id`, `type`,`dict_code`, `dict_name`,
`dict_value`, `desc`, `sort`, `enabled`) VALUES ('1529402613195784245',
'cacheType', 'CACHE_TYPE_REDIS', 'redis', 'redis', 'use redis to cache data',
1, 1);
INSERT IGNORE INTO `shenyu_dict` (`id`, `type`,`dict_code`, `dict_name`,
`dict_value`, `desc`, `sort`, `enabled`) VALUES ('1529402613195784246',
'threadpool', 'THREADPOOL', 'default', 'default', '', 5, 1);
+INSERT IGNORE INTO `shenyu_dict` (`id`, `type`,`dict_code`, `dict_name`,
`dict_value`, `desc`, `sort`, `enabled`) VALUES ('1529402613195784247',
'signRequestBody', 'SIGN_REQUEST_BODY', 'close', 'false', 'close', 1, 1);
+INSERT IGNORE INTO `shenyu_dict` (`id`, `type`,`dict_code`, `dict_name`,
`dict_value`, `desc`, `sort`, `enabled`) VALUES ('1529402613195784248',
'signRequestBody', 'SIGN_REQUEST_BODY', 'open', 'true', 'open', 0, 1);
/*plugin*/
INSERT IGNORE INTO `plugin` (`id`, `name`, `role`, `sort`, `enabled`) VALUES
('1','sign','Authentication', 20, '0');
@@ -554,6 +556,7 @@ INSERT IGNORE INTO plugin_handle (`id`,
`plugin_id`,`field`,`label`,`data_type`,
INSERT IGNORE INTO plugin_handle (`id`,
`plugin_id`,`field`,`label`,`data_type`,`type`,`sort`,`ext_obj`) VALUES
('1529402613204172881', '32', 'compressAlg', 'compressAlg', 3, 3, 6,
'{"required":"0","defaultValue":"none"}');
INSERT IGNORE INTO plugin_handle (`id`,
`plugin_id`,`field`,`label`,`data_type`,`type`,`sort`,`ext_obj`) VALUES
('1529402613204172882', '32', 'index', 'index', 2, 1, 1,
'{"required":"0","defaultValue":"","placeholder":"optional"}');
INSERT IGNORE INTO plugin_handle (`id`,
`plugin_id`,`field`,`label`,`data_type`,`type`,`sort`,`ext_obj`) VALUES
('1529402613204172883', '32', 'sampleRate', 'sampleRate', 2, 1, 2,
'{"required":"0","defaultValue":"","placeholder":"optional,0,0.01~1"}');
+INSERT IGNORE INTO plugin_handle (`id`,
`plugin_id`,`field`,`label`,`data_type`,`type`,`sort`,`ext_obj`) VALUES
('1529402613204172884', '1', 'signRequestBody', 'signRequestBody', 3, 2, 9,
'{"required":"0","defaultValue":"false","placeholder":"signRequestBody","rule":""}');
diff --git
a/shenyu-common/src/main/java/org/apache/shenyu/common/utils/JsonUtils.java
b/shenyu-common/src/main/java/org/apache/shenyu/common/utils/JsonUtils.java
index 5d46ac7dc..ba2362bd7 100644
--- a/shenyu-common/src/main/java/org/apache/shenyu/common/utils/JsonUtils.java
+++ b/shenyu-common/src/main/java/org/apache/shenyu/common/utils/JsonUtils.java
@@ -107,6 +107,22 @@ public final class JsonUtils {
}
}
+ /**
+ * String to Map.
+ *
+ * @param json the object
+ * @return the converted map
+ */
+ public static Map<String, Object> jsonToMap(final String json) {
+ try {
+ final MapType mapType =
MAPPER.getTypeFactory().constructMapType(LinkedHashMap.class, String.class,
Object.class);
+ return MAPPER.readValue(json, mapType);
+ } catch (IOException e) {
+ LOG.warn("write to map error: " + json, e);
+ return new LinkedHashMap<>();
+ }
+ }
+
/**
* Remove class object.
*
diff --git
a/shenyu-common/src/test/java/org/apache/shenyu/common/utils/JsonUtilsTest.java
b/shenyu-common/src/test/java/org/apache/shenyu/common/utils/JsonUtilsTest.java
index e0eb96a56..8e2113e70 100644
---
a/shenyu-common/src/test/java/org/apache/shenyu/common/utils/JsonUtilsTest.java
+++
b/shenyu-common/src/test/java/org/apache/shenyu/common/utils/JsonUtilsTest.java
@@ -84,6 +84,12 @@ public final class JsonUtilsTest {
assertEquals(Constants.EMPTY_JSON, JsonUtils.toJson(o));
}
+ @Test
+ public void testJsonToMap() {
+ Map<String, Object> stringObjectMap =
JsonUtils.jsonToMap(EXPECTED_JSON);
+ assertEquals(stringObjectMap.get("name"), "test object");
+ }
+
@Test
public void removeClass() {
Map<String, Map<String, String>> testMap = new HashMap<>();
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/combination/SignPluginTest.java
b/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/combination/SignPluginTest.java
index 955bac1d6..11332322d 100644
---
a/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/combination/SignPluginTest.java
+++
b/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/combination/SignPluginTest.java
@@ -19,6 +19,7 @@ package org.apache.shenyu.integrated.test.http.combination;
import com.google.common.collect.Maps;
import com.google.gson.reflect.TypeToken;
+import org.apache.commons.lang3.ObjectUtils;
import org.apache.shenyu.common.dto.AuthParamData;
import org.apache.shenyu.common.dto.AuthPathData;
import org.apache.shenyu.common.dto.ConditionData;
@@ -32,12 +33,12 @@ import org.apache.shenyu.integratedtest.common.dto.UserDTO;
import org.apache.shenyu.integratedtest.common.helper.HttpHelper;
import org.apache.shenyu.web.controller.LocalPluginController.RuleLocalData;
import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -52,20 +53,16 @@ public final class SignPluginTest extends
AbstractPluginDataInit {
private static final String APP_SECRET =
"061521A73DD94A3FA873C25D050685BB";
- @BeforeAll
- public static void setup() throws IOException {
+ @Test
+ public void testSign() throws Exception {
String authResult = initAuthData(APP_KEY, APP_SECRET,
buildAuthParamDataList(), buildAuthPathDataList());
assertThat(authResult, is("success"));
String pluginResult = initPlugin(PluginEnum.SIGN.getName(), null);
assertThat(pluginResult, is("success"));
String selectorAndRulesResult =
initSelectorAndRules(PluginEnum.SIGN.getName(), "",
buildSelectorConditionList(), buildRuleLocalDataList());
assertThat(selectorAndRulesResult, is("success"));
- }
-
- @Test
- public void testSign() throws Exception {
final String path = "/http/test/path/456";
- final String testUrlPath = "/http/test/path/456?name=Lee";
+ final String testUrlPath = "/http/test/path/456?name=Lee&data=3";
final String version = "1.0.0";
String now =
String.valueOf(LocalDateTime.now().toInstant(ZoneOffset.of("+0")).toEpochMilli());
Map<String, Object> normalHeaders = buildHeadersMap(now, path,
APP_KEY, APP_SECRET, version);
@@ -110,6 +107,72 @@ public final class SignPluginTest extends
AbstractPluginDataInit {
assertEquals("The signature timestamp has exceeded 5 minutes!",
rejectedErrorTimestampRespFuture.getMessage());
}
+ @Test
+ public void testSignRequestBody() throws Exception {
+
+ String authResult = initAuthData(APP_KEY, APP_SECRET,
buildAuthParamDataList(), buildAuthPathDataList());
+ assertThat(authResult, is("success"));
+ String pluginResult = initPlugin(PluginEnum.SIGN.getName(), null);
+ assertThat(pluginResult, is("success"));
+ String selectorAndRulesResult =
initSelectorAndRules(PluginEnum.SIGN.getName(), "",
+ buildSelectorConditionListOpenRequestBody(),
buildRuleLocalDataListRequestBody());
+ assertThat(selectorAndRulesResult, is("success"));
+ final String path = "/http/test/path/789";
+ final String testUrlPath = "/http/test/path/789?name=Lee&data=3";
+ final String version = "1.0.0";
+ String now =
String.valueOf(LocalDateTime.now().toInstant(ZoneOffset.of("+0")).toEpochMilli());
+ Map<String, String> requestBody = Maps.newHashMapWithExpectedSize(2);
+ requestBody.put("name", "Lee");
+ requestBody.put("data", "3");
+ Map<String, Object> normalHeaders = buildHeadersMapRequestBody(now,
path, APP_KEY, APP_SECRET, version, requestBody);
+ UserDTO normalRespFuture =
HttpHelper.INSTANCE.getFromGateway(testUrlPath, normalHeaders,
+ UserDTO.class);
+ assertEquals("Lee", normalRespFuture.getUserName());
+
+ Map<String, Object> errorPathHeaders = buildHeadersMapRequestBody(now,
"errorPath", APP_KEY, APP_SECRET, version, requestBody);
+ AdminResponse<Object> rejectedErrorPathRespFuture =
HttpHelper.INSTANCE.getFromGateway(testUrlPath,
+ errorPathHeaders,
+ new TypeToken<AdminResponse<Object>>() {
+ }.getType());
+ assertEquals("signature value is error!",
rejectedErrorPathRespFuture.getMessage());
+
+ Map<String, Object> errorAppKeyHeaders =
buildHeadersMapRequestBody(now, path, "ERRORKEY", APP_SECRET, version,
requestBody);
+ AdminResponse<Object> rejectedErrorAKRespFuture =
HttpHelper.INSTANCE.getFromGateway(testUrlPath,
+ errorAppKeyHeaders,
+ new TypeToken<AdminResponse<Object>>() {
+ }.getType());
+ assertEquals("sign appKey does not exist.",
rejectedErrorAKRespFuture.getMessage());
+
+ Map<String, Object> errorAppSecretHeaders =
buildHeadersMapRequestBody(now, path, APP_KEY, "ERRORSECRET", version,
requestBody);
+ AdminResponse<Object> rejectedErrorSKRespFuture =
HttpHelper.INSTANCE.getFromGateway(testUrlPath,
+ errorAppSecretHeaders,
+ new TypeToken<AdminResponse<Object>>() {
+ }.getType());
+ assertEquals("signature value is error!",
rejectedErrorSKRespFuture.getMessage());
+
+ Map<String, Object> errorVersionHeaders =
buildHeadersMapRequestBody(now, path, APP_KEY, APP_SECRET, "1.0.2",
requestBody);
+ AdminResponse<Object> rejectedErrorVersionRespFuture =
HttpHelper.INSTANCE.getFromGateway(testUrlPath,
+ errorVersionHeaders,
+ new TypeToken<AdminResponse<Object>>() {
+ }.getType());
+ assertEquals("signature value is error!",
rejectedErrorVersionRespFuture.getMessage());
+
+ Map<String, Object> errorRequestBody = buildHeadersMapRequestBody(now,
path, APP_KEY, APP_SECRET, "1.0.0", null);
+ AdminResponse<Object> rejectedErrorRequestBodyRespFuture =
HttpHelper.INSTANCE.getFromGateway(testUrlPath,
+ errorRequestBody,
+ new TypeToken<AdminResponse<Object>>() {
+ }.getType());
+ assertEquals("signature value is error!",
rejectedErrorRequestBodyRespFuture.getMessage());
+
+ String errorTime =
String.valueOf(LocalDateTime.now().toInstant(ZoneOffset.of("+0")).toEpochMilli()
- 360000);
+ Map<String, Object> errorTimestampHeaders =
buildHeadersMapRequestBody(errorTime, path, APP_KEY, APP_SECRET, version,
requestBody);
+ AdminResponse<Object> rejectedErrorTimestampRespFuture =
HttpHelper.INSTANCE.getFromGateway(testUrlPath,
+ errorTimestampHeaders,
+ new TypeToken<AdminResponse<Object>>() {
+ }.getType());
+ assertEquals("The signature timestamp has exceeded 5 minutes!",
rejectedErrorTimestampRespFuture.getMessage());
+ }
+
private Map<String, Object> buildHeadersMap(final String timestamp, final
String path, final String appKey,
final String appSecret, final
String version) {
Map<String, String> params = Maps.newHashMapWithExpectedSize(3);
@@ -126,6 +189,25 @@ public final class SignPluginTest extends
AbstractPluginDataInit {
return headers;
}
+ private Map<String, Object> buildHeadersMapRequestBody(final String
timestamp, final String path, final String appKey,
+ final String appSecret, final
String version, final Map<String, String> requestBody) {
+ Map<String, String> params = Maps.newHashMapWithExpectedSize(3);
+ params.put("timestamp", timestamp);
+ params.put("path", path);
+ params.put("version", version);
+ if (!ObjectUtils.isEmpty(requestBody)) {
+ params.putAll(requestBody);
+ }
+ String sign = SignUtils.generateSign(appSecret, params);
+
+ Map<String, Object> headers = Maps.newHashMapWithExpectedSize(4);
+ headers.put("timestamp", timestamp);
+ headers.put("appKey", appKey);
+ headers.put("sign", sign);
+ headers.put("version", version);
+ return headers;
+ }
+
private static List<AuthParamData> buildAuthParamDataList() {
AuthParamData authParamData = new AuthParamData();
authParamData.setAppName("http-sign");
@@ -138,7 +220,11 @@ public final class SignPluginTest extends
AbstractPluginDataInit {
authPathData.setAppName("http-sign");
authPathData.setPath("/http/test/path/456");
authPathData.setEnabled(true);
- return Collections.singletonList(authPathData);
+ AuthPathData authPathData2 = new AuthPathData();
+ authPathData2.setAppName("http-sign");
+ authPathData2.setPath("/http/test/path/789");
+ authPathData2.setEnabled(true);
+ return Arrays.asList(authPathData, authPathData2);
}
private static List<ConditionData> buildSelectorConditionList() {
@@ -149,6 +235,14 @@ public final class SignPluginTest extends
AbstractPluginDataInit {
return Collections.singletonList(conditionData);
}
+ private static List<ConditionData>
buildSelectorConditionListOpenRequestBody() {
+ ConditionData conditionData2 = new ConditionData();
+ conditionData2.setParamType(ParamTypeEnum.URI.getName());
+ conditionData2.setOperator(OperatorEnum.EQ.getAlias());
+ conditionData2.setParamValue("/http/test/path/789");
+ return Collections.singletonList(conditionData2);
+ }
+
private static List<RuleLocalData> buildRuleLocalDataList() {
final RuleLocalData ruleLocalData = new RuleLocalData();
ConditionData conditionData = new ConditionData();
@@ -156,9 +250,21 @@ public final class SignPluginTest extends
AbstractPluginDataInit {
conditionData.setOperator(OperatorEnum.EQ.getAlias());
conditionData.setParamValue("/http/test/path/456");
ruleLocalData.setConditionDataList(Collections.singletonList(conditionData));
+ ruleLocalData.setRuleHandler("{\"signRequestBody\": false}");
return Collections.singletonList(ruleLocalData);
}
+ private static List<RuleLocalData> buildRuleLocalDataListRequestBody() {
+ final RuleLocalData ruleLocalData2 = new RuleLocalData();
+ ConditionData conditionData2 = new ConditionData();
+ conditionData2.setParamType(ParamTypeEnum.URI.getName());
+ conditionData2.setOperator(OperatorEnum.EQ.getAlias());
+ conditionData2.setParamValue("/http/test/path/789");
+
ruleLocalData2.setConditionDataList(Collections.singletonList(conditionData2));
+ ruleLocalData2.setRuleHandler("{\"signRequestBody\": true}");
+ return Collections.singletonList(ruleLocalData2);
+ }
+
@AfterAll
public static void clean() throws IOException {
cleanPluginData(PluginEnum.SIGN.getName());
diff --git
a/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/SignPlugin.java
b/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/SignPlugin.java
index 9c1b0350c..46e65c059 100644
---
a/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/SignPlugin.java
+++
b/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/SignPlugin.java
@@ -17,24 +17,49 @@
package org.apache.shenyu.plugin.sign;
+import com.google.common.collect.Maps;
+import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.shenyu.common.dto.RuleData;
import org.apache.shenyu.common.dto.SelectorData;
import org.apache.shenyu.common.enums.PluginEnum;
+import org.apache.shenyu.common.utils.JsonUtils;
+import org.apache.shenyu.plugin.api.ShenyuPluginChain;
import org.apache.shenyu.plugin.api.result.ShenyuResultEnum;
import org.apache.shenyu.plugin.api.result.ShenyuResultWrap;
-import org.apache.shenyu.plugin.api.ShenyuPluginChain;
-import org.apache.shenyu.plugin.base.AbstractShenyuPlugin;
import org.apache.shenyu.plugin.api.utils.WebFluxResultUtils;
+import org.apache.shenyu.plugin.base.AbstractShenyuPlugin;
+import org.apache.shenyu.plugin.base.support.BodyInserterContext;
+import org.apache.shenyu.plugin.base.support.CachedBodyOutputMessage;
+import org.apache.shenyu.plugin.base.utils.CacheKeyUtils;
+import org.apache.shenyu.plugin.base.utils.ResponseUtils;
import org.apache.shenyu.plugin.sign.api.SignService;
+import org.apache.shenyu.plugin.sign.decorator.SignRequestDecorator;
+import org.apache.shenyu.plugin.sign.handler.SignPluginDataHandler;
+import org.apache.shenyu.plugin.sign.handler.SignRuleHandler;
+import org.springframework.http.ReactiveHttpOutputMessage;
+import org.springframework.http.codec.HttpMessageReader;
+import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
+import org.springframework.util.MultiValueMap;
+import org.springframework.util.ObjectUtils;
+import org.springframework.web.reactive.function.BodyInserter;
+import org.springframework.web.reactive.function.BodyInserters;
+import org.springframework.web.reactive.function.server.HandlerStrategies;
+import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
/**
* Sign Plugin.
*/
public class SignPlugin extends AbstractShenyuPlugin {
+ private static final List<HttpMessageReader<?>> MESSAGE_READERS =
HandlerStrategies.builder().build().messageReaders();
+
private final SignService signService;
/**
@@ -57,12 +82,46 @@ public class SignPlugin extends AbstractShenyuPlugin {
}
@Override
- protected Mono<Void> doExecute(final ServerWebExchange exchange, final
ShenyuPluginChain chain, final SelectorData selector, final RuleData rule) {
+ @SuppressWarnings("unchecked")
+ protected Mono<Void> doExecute(final ServerWebExchange exchange, final
ShenyuPluginChain chain, final SelectorData selectorData, final RuleData rule) {
+ SignRuleHandler ruleHandler =
SignPluginDataHandler.CACHED_HANDLE.get().obtainHandle(CacheKeyUtils.INST.getKey(rule));
+ if (!ObjectUtils.isEmpty(ruleHandler) &&
ruleHandler.getSignRequestBody()) {
+ ServerRequest serverRequest = ServerRequest.create(exchange,
MESSAGE_READERS);
+ Mono<String> mono = serverRequest.bodyToMono(String.class)
+ .switchIfEmpty(Mono.defer(() -> Mono.just("")))
+ .flatMap(originalBody -> signBody(originalBody, exchange));
+
+ BodyInserter<Mono<String>, ReactiveHttpOutputMessage> bodyInserter
= BodyInserters.fromPublisher(mono, String.class);
+ CachedBodyOutputMessage outputMessage =
ResponseUtils.newCachedBodyOutputMessage(exchange);
+ return bodyInserter.insert(outputMessage, new
BodyInserterContext())
+ .then(Mono.defer(() -> {
+ ServerHttpRequestDecorator decorator = new
SignRequestDecorator(exchange, outputMessage);
+ return
chain.execute(exchange.mutate().request(decorator).build());
+ })).onErrorResume((Function<Throwable, Mono<Void>>)
throwable -> ResponseUtils.release(outputMessage, throwable));
+ }
Pair<Boolean, String> result = signService.signVerify(exchange);
if (Boolean.FALSE.equals(result.getLeft())) {
Object error = ShenyuResultWrap.error(exchange,
ShenyuResultEnum.SIGN_IS_NOT_PASS.getCode(), result.getRight(), null);
return WebFluxResultUtils.result(exchange, error);
}
+
return chain.execute(exchange);
+
+ }
+
+ @SuppressWarnings("rawtypes")
+ private Mono signBody(final String originalBody, final ServerWebExchange
exchange) {
+ // get url params
+ MultiValueMap<String, String> queryParams =
exchange.getRequest().getQueryParams();
+ // get post body
+ Map<String, Object> requestBody = StringUtils.isBlank(originalBody) ?
Maps.newHashMapWithExpectedSize(4) : JsonUtils.jsonToMap(originalBody);
+ requestBody.putAll(queryParams.toSingleValueMap());
+ Pair<Boolean, String> result = signService.signVerify(exchange,
requestBody);
+ if (Boolean.FALSE.equals(result.getLeft())) {
+ Object error = ShenyuResultWrap.error(exchange,
ShenyuResultEnum.SIGN_IS_NOT_PASS.getCode(), result.getRight(), null);
+ return WebFluxResultUtils.result(exchange, error);
+ }
+ // return original data
+ return Mono.just(originalBody);
}
}
diff --git
a/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/api/SignService.java
b/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/api/SignService.java
index d75d382d4..ae4c2e1db 100644
---
a/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/api/SignService.java
+++
b/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/api/SignService.java
@@ -20,15 +20,27 @@ package org.apache.shenyu.plugin.sign.api;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.web.server.ServerWebExchange;
+import java.util.Map;
+
/**
* The interface Sign service.
*/
public interface SignService {
+ /**
+ * Sign verify pair.
+ * @param exchange the exchange
+ * @param requestBody the requestBody
+ * @return the pair
+ */
+ Pair<Boolean, String> signVerify(ServerWebExchange exchange, Map<String,
Object> requestBody);
+
/**
* Sign verify pair.
* @param exchange the exchange
* @return the pair
*/
- Pair<Boolean, String> signVerify(ServerWebExchange exchange);
+ default Pair<Boolean, String> signVerify(ServerWebExchange exchange) {
+ return signVerify(exchange, null);
+ }
}
diff --git
a/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/decorator/SignRequestDecorator.java
b/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/decorator/SignRequestDecorator.java
new file mode 100644
index 000000000..ef61569af
--- /dev/null
+++
b/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/decorator/SignRequestDecorator.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.sign.decorator;
+
+import org.apache.shenyu.plugin.base.support.CachedBodyOutputMessage;
+import org.apache.shenyu.plugin.base.utils.ResponseUtils;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Flux;
+import reactor.util.annotation.NonNull;
+
+/**
+ * Build and modify the request class.
+ */
+public class SignRequestDecorator extends ServerHttpRequestDecorator {
+
+ private final CachedBodyOutputMessage cachedBodyOutputMessage;
+
+ private final ServerWebExchange exchange;
+
+ public SignRequestDecorator(final ServerWebExchange exchange,
+ final CachedBodyOutputMessage
cachedBodyOutputMessage) {
+ super(exchange.getRequest());
+ this.cachedBodyOutputMessage = cachedBodyOutputMessage;
+ this.exchange = exchange;
+ }
+
+ @Override
+ @NonNull
+ public Flux<DataBuffer> getBody() {
+ return cachedBodyOutputMessage.getBody();
+ }
+
+ @Override
+ @NonNull
+ public HttpHeaders getHeaders() {
+ return
ResponseUtils.chunkedHeader(this.exchange.getRequest().getHeaders());
+ }
+}
diff --git
a/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/handler/SignPluginDataHandler.java
b/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/handler/SignPluginDataHandler.java
new file mode 100644
index 000000000..542aac492
--- /dev/null
+++
b/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/handler/SignPluginDataHandler.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.sign.handler;
+
+import org.apache.shenyu.common.dto.RuleData;
+import org.apache.shenyu.common.enums.PluginEnum;
+import org.apache.shenyu.common.utils.GsonUtils;
+import org.apache.shenyu.plugin.base.cache.CommonHandleCache;
+import org.apache.shenyu.plugin.base.handler.PluginDataHandler;
+import org.apache.shenyu.plugin.base.utils.BeanHolder;
+import org.apache.shenyu.plugin.base.utils.CacheKeyUtils;
+
+import java.util.Optional;
+import java.util.function.Supplier;
+
+/**
+ * The type sign plugin plugin data subscriber.
+ */
+public class SignPluginDataHandler implements PluginDataHandler {
+
+ public static final Supplier<CommonHandleCache<String, SignRuleHandler>>
CACHED_HANDLE = new BeanHolder<>(CommonHandleCache::new);
+
+ @Override
+ public void handlerRule(final RuleData ruleData) {
+ Optional.ofNullable(ruleData.getHandle()).ifPresent(s -> {
+ SignRuleHandler cryptorRuleHandler =
GsonUtils.getInstance().fromJson(s, SignRuleHandler.class);
+
CACHED_HANDLE.get().cachedHandle(CacheKeyUtils.INST.getKey(ruleData),
cryptorRuleHandler);
+ });
+ }
+
+ @Override
+ public void removeRule(final RuleData ruleData) {
+ Optional.ofNullable(ruleData.getHandle()).ifPresent(s ->
CACHED_HANDLE.get().removeHandle(CacheKeyUtils.INST.getKey(ruleData)));
+ }
+
+ @Override
+ public String pluginNamed() {
+ return PluginEnum.SIGN.getName();
+ }
+}
diff --git
a/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/subscriber/SignAuthDataSubscriber.java
b/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/handler/SignRuleHandler.java
similarity index 54%
copy from
shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/subscriber/SignAuthDataSubscriber.java
copy to
shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/handler/SignRuleHandler.java
index 16571807e..3f536eb4a 100644
---
a/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/subscriber/SignAuthDataSubscriber.java
+++
b/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/handler/SignRuleHandler.java
@@ -15,24 +15,36 @@
* limitations under the License.
*/
-package org.apache.shenyu.plugin.sign.subscriber;
+package org.apache.shenyu.plugin.sign.handler;
-import org.apache.shenyu.plugin.sign.cache.SignAuthDataCache;
-import org.apache.shenyu.sync.data.api.AuthDataSubscriber;
-import org.apache.shenyu.common.dto.AppAuthData;
+import org.apache.shenyu.common.dto.convert.rule.RuleHandle;
/**
- * The type Sign auth data subscriber.
+ * Sign rule handle.
*/
-public class SignAuthDataSubscriber implements AuthDataSubscriber {
-
- @Override
- public void onSubscribe(final AppAuthData appAuthData) {
- SignAuthDataCache.getInstance().cacheAuthData(appAuthData);
+public class SignRuleHandler implements RuleHandle {
+
+ private boolean signRequestBody;
+
+ /**
+ * get getSignRequestBody.
+ * @return boolean
+ */
+ public boolean getSignRequestBody() {
+ return signRequestBody;
}
-
+
+ /**
+ * set signRequestBody.
+ * @param signRequestBody signRequestBody
+ */
+ public void setSignRequestBody(final boolean signRequestBody) {
+ this.signRequestBody = signRequestBody;
+ }
+
@Override
- public void unSubscribe(final AppAuthData appAuthData) {
- SignAuthDataCache.getInstance().removeAuthData(appAuthData);
+ public String toString() {
+ return "SignRuleHandler{"
+ + "signRequestBody=" + signRequestBody + '}';
}
}
diff --git
a/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/service/DefaultSignService.java
b/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/service/DefaultSignService.java
index 2e7c68be1..bcbf58dc7 100644
---
a/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/service/DefaultSignService.java
+++
b/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/service/DefaultSignService.java
@@ -35,6 +35,7 @@ import org.apache.shenyu.plugin.sign.cache.SignAuthDataCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.util.ObjectUtils;
import org.springframework.web.server.ServerWebExchange;
import java.time.LocalDateTime;
@@ -53,13 +54,13 @@ public class DefaultSignService implements SignService {
private int delay;
@Override
- public Pair<Boolean, String> signVerify(final ServerWebExchange exchange) {
+ public Pair<Boolean, String> signVerify(final ServerWebExchange exchange,
final Map<String, Object> requestBody) {
final ShenyuContext shenyuContext =
exchange.getAttribute(Constants.CONTEXT);
assert shenyuContext != null;
- return verify(shenyuContext, exchange);
+ return verify(shenyuContext, exchange, requestBody);
}
- private Pair<Boolean, String> verify(final ShenyuContext shenyuContext,
final ServerWebExchange exchange) {
+ private Pair<Boolean, String> verify(final ShenyuContext shenyuContext,
final ServerWebExchange exchange, final Map<String, Object> requestBody) {
if (StringUtils.isBlank(shenyuContext.getAppKey())
|| StringUtils.isBlank(shenyuContext.getSign())
|| StringUtils.isBlank(shenyuContext.getTimestamp())) {
@@ -72,7 +73,8 @@ public class DefaultSignService implements SignService {
if (between > delay) {
return Pair.of(Boolean.FALSE,
String.format(ShenyuResultEnum.SIGN_TIME_IS_TIMEOUT.getMsg(), delay));
}
- return sign(shenyuContext, exchange);
+
+ return sign(shenyuContext, exchange, requestBody);
}
/**
@@ -81,7 +83,7 @@ public class DefaultSignService implements SignService {
* @param shenyuContext {@linkplain ShenyuContext}
* @return result : True is pass, False is not pass.
*/
- private Pair<Boolean, String> sign(final ShenyuContext shenyuContext,
final ServerWebExchange exchange) {
+ private Pair<Boolean, String> sign(final ShenyuContext shenyuContext,
final ServerWebExchange exchange, final Map<String, Object> requestBody) {
final AppAuthData appAuthData =
SignAuthDataCache.getInstance().obtainAuthData(shenyuContext.getAppKey());
if (Objects.isNull(appAuthData) ||
Boolean.FALSE.equals(appAuthData.getEnabled())) {
LOG.error("sign APP_kEY does not exist or has been disabled,{}",
shenyuContext.getAppKey());
@@ -101,7 +103,7 @@ public class DefaultSignService implements SignService {
return Pair.of(Boolean.FALSE, Constants.SIGN_PATH_NOT_EXIST);
}
}
- String sigKey =
ShenyuSignProviderWrap.generateSign(appAuthData.getAppSecret(),
buildParamsMap(shenyuContext));
+ String sigKey =
ShenyuSignProviderWrap.generateSign(appAuthData.getAppSecret(),
buildParamsMap(shenyuContext, requestBody));
boolean result = Objects.equals(sigKey, shenyuContext.getSign());
if (!result) {
LOG.error("the SignUtils generated signature value is:{},the
accepted value is:{}", sigKey, shenyuContext.getSign());
@@ -121,11 +123,16 @@ public class DefaultSignService implements SignService {
return Pair.of(Boolean.TRUE, "");
}
- private Map<String, String> buildParamsMap(final ShenyuContext
shenyuContext) {
+ private Map<String, String> buildParamsMap(final ShenyuContext
shenyuContext, final Map<String, Object> requestBody) {
Map<String, String> map = Maps.newHashMapWithExpectedSize(3);
map.put(Constants.TIMESTAMP, shenyuContext.getTimestamp());
map.put(Constants.PATH, shenyuContext.getPath());
map.put(Constants.VERSION, "1.0.0");
+ if (!ObjectUtils.isEmpty(requestBody)) {
+ requestBody.forEach((key, value) -> {
+ map.putIfAbsent(key, Objects.toString(value, null));
+ });
+ }
return map;
}
}
diff --git
a/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/subscriber/SignAuthDataSubscriber.java
b/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/subscriber/SignAuthDataSubscriber.java
index 16571807e..d360786f7 100644
---
a/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/subscriber/SignAuthDataSubscriber.java
+++
b/shenyu-plugin/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/subscriber/SignAuthDataSubscriber.java
@@ -17,9 +17,9 @@
package org.apache.shenyu.plugin.sign.subscriber;
+import org.apache.shenyu.common.dto.AppAuthData;
import org.apache.shenyu.plugin.sign.cache.SignAuthDataCache;
import org.apache.shenyu.sync.data.api.AuthDataSubscriber;
-import org.apache.shenyu.common.dto.AppAuthData;
/**
* The type Sign auth data subscriber.
diff --git
a/shenyu-plugin/shenyu-plugin-sign/src/test/java/org/apache/shenyu/plugin/sign/SignPluginTest.java
b/shenyu-plugin/shenyu-plugin-sign/src/test/java/org/apache/shenyu/plugin/sign/SignPluginTest.java
index cfc5de0ef..68e9700f8 100644
---
a/shenyu-plugin/shenyu-plugin-sign/src/test/java/org/apache/shenyu/plugin/sign/SignPluginTest.java
+++
b/shenyu-plugin/shenyu-plugin-sign/src/test/java/org/apache/shenyu/plugin/sign/SignPluginTest.java
@@ -17,11 +17,19 @@
package org.apache.shenyu.plugin.sign;
+import com.google.common.collect.Maps;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.shenyu.common.dto.RuleData;
import org.apache.shenyu.common.dto.SelectorData;
+import org.apache.shenyu.common.enums.PluginEnum;
import org.apache.shenyu.plugin.api.ShenyuPluginChain;
+import org.apache.shenyu.plugin.api.result.DefaultShenyuResult;
+import org.apache.shenyu.plugin.api.result.ShenyuResult;
+import org.apache.shenyu.plugin.api.utils.SpringBeanUtils;
import org.apache.shenyu.plugin.sign.api.SignService;
+import org.apache.shenyu.plugin.sign.handler.SignPluginDataHandler;
+import org.apache.shenyu.plugin.sign.handler.SignRuleHandler;
+import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -29,12 +37,22 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
+import java.io.IOException;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -51,19 +69,99 @@ public final class SignPluginTest {
private SignPlugin signPlugin;
+ private RuleData ruleData;
+
+ private SignService signService;
+
+ private SignPluginDataHandler signPluginDataHandler;
+
@BeforeEach
public void setup() {
+
+ this.ruleData = new RuleData();
+ this.ruleData.setSelectorId("test-sign");
+ this.ruleData.setName("test-sign-plugin");
+ this.signPluginDataHandler = new SignPluginDataHandler();
+ signService = mock(SignService.class);
+ this.signPlugin = new SignPlugin(signService);
+
+ ConfigurableApplicationContext context =
mock(ConfigurableApplicationContext.class);
+ SpringBeanUtils.getInstance().setApplicationContext(context);
+ when(context.getBean(ShenyuResult.class)).thenReturn(new
DefaultShenyuResult());
+ assertEquals(signPlugin.getOrder(), PluginEnum.SIGN.getCode());
+ assertEquals(signPlugin.named(), PluginEnum.SIGN.getName());
+ assertEquals(signPluginDataHandler.pluginNamed(),
PluginEnum.SIGN.getName());
+
+ SignRuleHandler signRuleHandler = new SignRuleHandler();
+ signRuleHandler.setSignRequestBody(true);
+ assertTrue(signRuleHandler.toString().contains("signRequestBody"));
+ assertTrue(signRuleHandler.getSignRequestBody());
+ }
+
+ @Test
+ public void testSignPluginSimple() {
this.exchange =
MockServerWebExchange.from(MockServerHttpRequest.get("localhost").build());
- SignService signService = mock(SignService.class);
+
when(signService.signVerify(exchange)).thenReturn(Pair.of(true, ""));
- this.signPlugin = new SignPlugin(signService);
+ RuleData data = mock(RuleData.class);
+ SelectorData selectorData = mock(SelectorData.class);
+ when(chain.execute(exchange)).thenReturn(Mono.empty());
+ StepVerifier.create(signPlugin.doExecute(exchange, chain,
selectorData, data)).expectSubscription().verifyComplete();
}
@Test
- public void testSignPlugin() {
+ public void testSignPluginSimple2() {
+ this.exchange =
MockServerWebExchange.from(MockServerHttpRequest.get("localhost").build());
+
+ when(signService.signVerify(exchange)).thenReturn(Pair.of(false, ""));
RuleData data = mock(RuleData.class);
SelectorData selectorData = mock(SelectorData.class);
when(chain.execute(exchange)).thenReturn(Mono.empty());
StepVerifier.create(signPlugin.doExecute(exchange, chain,
selectorData, data)).expectSubscription().verifyComplete();
}
+
+ @Test
+ public void testSignPluginSignBody() {
+ this.ruleData.setHandle("{\"signRequestBody\": true}");
+ this.exchange = MockServerWebExchange.from(MockServerHttpRequest
+ .method(HttpMethod.POST, "/test")
+ .header(HttpHeaders.CONTENT_TYPE,
MediaType.APPLICATION_JSON_VALUE)
+ .body("{\"data\": "
+ + "\"3\""
+ + "}"));
+ Map<String, Object> requestBody = Maps.newHashMapWithExpectedSize(1);
+ requestBody.put("data", "3");
+ when(signService.signVerify(exchange,
requestBody)).thenReturn(Pair.of(true, ""));
+ when(this.chain.execute(any())).thenReturn(Mono.empty());
+ SelectorData selectorData = mock(SelectorData.class);
+ signPluginDataHandler.handlerRule(ruleData);
+ StepVerifier.create(signPlugin.doExecute(this.exchange, this.chain,
selectorData, this.ruleData)).expectSubscription().verifyComplete();
+
+ }
+
+ @Test
+ public void testSignPluginSignBody2() {
+ this.ruleData.setHandle("{\"signRequestBody\": true}");
+
+ this.exchange = MockServerWebExchange.from(MockServerHttpRequest
+ .method(HttpMethod.POST, "/test")
+ .header(HttpHeaders.CONTENT_TYPE,
MediaType.APPLICATION_JSON_VALUE)
+ .body("{\"data\": "
+ + "\"4\""
+ + "}"));
+ Map<String, Object> requestBody = Maps.newHashMapWithExpectedSize(1);
+ requestBody.put("data", "4");
+ when(signService.signVerify(exchange,
requestBody)).thenReturn(Pair.of(false, ""));
+ when(this.chain.execute(any())).thenReturn(Mono.empty());
+ SelectorData selectorData = mock(SelectorData.class);
+ signPluginDataHandler.handlerRule(ruleData);
+ StepVerifier.create(signPlugin.doExecute(this.exchange, this.chain,
selectorData, this.ruleData)).expectSubscription().verifyComplete();
+
+ }
+
+ @AfterEach
+ public void clean() throws IOException {
+ signPluginDataHandler.removeRule(this.ruleData);
+ }
+
}
diff --git
a/shenyu-plugin/shenyu-plugin-sign/src/test/java/org/apache/shenyu/plugin/sign/service/DefaultSignServiceTest.java
b/shenyu-plugin/shenyu-plugin-sign/src/test/java/org/apache/shenyu/plugin/sign/service/DefaultSignServiceTest.java
index 404120701..81cd4c603 100644
---
a/shenyu-plugin/shenyu-plugin-sign/src/test/java/org/apache/shenyu/plugin/sign/service/DefaultSignServiceTest.java
+++
b/shenyu-plugin/shenyu-plugin-sign/src/test/java/org/apache/shenyu/plugin/sign/service/DefaultSignServiceTest.java
@@ -22,6 +22,7 @@ import com.google.common.collect.Maps;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.shenyu.common.constant.Constants;
import org.apache.shenyu.common.dto.AppAuthData;
+import org.apache.shenyu.common.dto.AuthParamData;
import org.apache.shenyu.common.dto.AuthPathData;
import org.apache.shenyu.common.dto.PluginData;
import org.apache.shenyu.common.enums.PluginEnum;
@@ -182,6 +183,30 @@ public final class DefaultSignServiceTest {
assertEquals(ret, Pair.of(false, Constants.SIGN_PATH_NOT_EXIST));
}
+ @Test
+ public void fillParamPath() {
+ this.exchange.getAttributes().put(Constants.CONTEXT, this.passed);
+ AppAuthData authData =
SignAuthDataCache.getInstance().obtainAuthData(appKey);
+ AuthParamData authParamData = new AuthParamData();
+ authParamData.setAppParam("appParam");
+ authParamData.setAppName("appParam");
+ authData.setParamDataList(Collections.singletonList(authParamData));
+ SignAuthDataCache.getInstance().cacheAuthData(authData);
+
+ Pair<Boolean, String> ret = this.signService.signVerify(this.exchange);
+ assertEquals(ret, Pair.of(true, ""));
+ }
+
+ @Test
+ public void emptyParamPath() {
+ this.exchange.getAttributes().put(Constants.CONTEXT, this.passed);
+ AppAuthData authData =
SignAuthDataCache.getInstance().obtainAuthData(appKey);
+ SignAuthDataCache.getInstance().cacheAuthData(authData);
+
+ Pair<Boolean, String> ret = this.signService.signVerify(this.exchange);
+ assertEquals(ret, Pair.of(true, ""));
+ }
+
@Test
public void errorAuthPath() {
this.passed.setPath("errorPath");
@@ -201,6 +226,16 @@ public final class DefaultSignServiceTest {
assertEquals(ret, Pair.of(false, Constants.SIGN_VALUE_IS_ERROR));
}
+ @Test
+ public void bodySign() {
+ this.passed.setSign("errorSign");
+ this.exchange.getAttributes().put(Constants.CONTEXT, this.passed);
+ Map<String, Object> requestBody = Maps.newHashMapWithExpectedSize(1);
+ requestBody.put("data", "data");
+ Pair<Boolean, String> ret = this.signService.signVerify(this.exchange,
requestBody);
+ assertEquals(ret, Pair.of(false, Constants.SIGN_VALUE_IS_ERROR));
+ }
+
private String buildSign(final String signKey, final String timeStamp,
final String path) {
Map<String, String> map = Maps.newHashMapWithExpectedSize(3);
map.put(Constants.TIMESTAMP, timeStamp);
diff --git
a/shenyu-plugin/shenyu-plugin-sign/src/test/java/org/apache/shenyu/plugin/sign/subscriber/SignAuthDataSubscriberTest.java
b/shenyu-plugin/shenyu-plugin-sign/src/test/java/org/apache/shenyu/plugin/sign/subscriber/SignAuthDataSubscriberTest.java
new file mode 100644
index 000000000..d56e3dab0
--- /dev/null
+++
b/shenyu-plugin/shenyu-plugin-sign/src/test/java/org/apache/shenyu/plugin/sign/subscriber/SignAuthDataSubscriberTest.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.sign.subscriber;
+
+import org.apache.shenyu.common.dto.AppAuthData;
+import org.apache.shenyu.plugin.sign.cache.SignAuthDataCache;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+ * The Shenyu default sign subscriber test.
+ */
+@ExtendWith(MockitoExtension.class)
+public class SignAuthDataSubscriberTest {
+
+ private SignAuthDataSubscriber signAuthDataSubscriber;
+
+ @BeforeEach
+ public void setUp() {
+ signAuthDataSubscriber = new SignAuthDataSubscriber();
+ }
+
+ @Test
+ void onSubscribe() {
+ AppAuthData appAuthData = new AppAuthData();
+ appAuthData.setAppKey("D9FD95F496C9495DB5604222A13C3D08");
+ appAuthData.setAppSecret("02D25048AA1E466F8920E68B08E668DE");
+ appAuthData.setEnabled(true);
+ signAuthDataSubscriber.onSubscribe(appAuthData);
+
assertEquals(SignAuthDataCache.getInstance().obtainAuthData("D9FD95F496C9495DB5604222A13C3D08"),
appAuthData);
+ }
+
+ @Test
+ void unSubscribe() {
+ AppAuthData appAuthData = new AppAuthData();
+ appAuthData.setAppKey("D9FD95F496C9495DB5604222A13C3D08");
+ appAuthData.setAppSecret("02D25048AA1E466F8920E68B08E668DE");
+ appAuthData.setEnabled(true);
+ signAuthDataSubscriber.onSubscribe(appAuthData);
+ signAuthDataSubscriber.unSubscribe(appAuthData);
+
assertNull(SignAuthDataCache.getInstance().obtainAuthData("D9FD95F496C9495DB5604222A13C3D08"));
+ }
+}
diff --git
a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-sign/src/main/java/org/apache/shenyu/springboot/starter/plugin/sign/SignPluginConfiguration.java
b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-sign/src/main/java/org/apache/shenyu/springboot/starter/plugin/sign/SignPluginConfiguration.java
index 326f7b6f4..e6effe7d3 100644
---
a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-sign/src/main/java/org/apache/shenyu/springboot/starter/plugin/sign/SignPluginConfiguration.java
+++
b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-sign/src/main/java/org/apache/shenyu/springboot/starter/plugin/sign/SignPluginConfiguration.java
@@ -17,11 +17,13 @@
package org.apache.shenyu.springboot.starter.plugin.sign;
+import org.apache.shenyu.plugin.base.handler.PluginDataHandler;
import org.apache.shenyu.plugin.sign.api.DefaultSignProvider;
import org.apache.shenyu.plugin.sign.api.SignService;
import org.apache.shenyu.plugin.api.ShenyuPlugin;
import org.apache.shenyu.plugin.sign.api.SignProvider;
import org.apache.shenyu.plugin.sign.SignPlugin;
+import org.apache.shenyu.plugin.sign.handler.SignPluginDataHandler;
import org.apache.shenyu.plugin.sign.service.DefaultSignService;
import org.apache.shenyu.plugin.sign.subscriber.SignAuthDataSubscriber;
import org.apache.shenyu.sync.data.api.AuthDataSubscriber;
@@ -80,4 +82,14 @@ public class SignPluginConfiguration {
public AuthDataSubscriber signAuthDataSubscriber() {
return new SignAuthDataSubscriber();
}
+
+ /**
+ * sign plugin data handler.
+ *
+ * @return the plugin data handler
+ */
+ @Bean
+ public PluginDataHandler signPluginDataHandler() {
+ return new SignPluginDataHandler();
+ }
}