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();
+    }
 }

Reply via email to