From 242ce79393632bbffefa93c26128e36a7f729a77 Mon Sep 17 00:00:00 2001
From: zhangdaiscott <zhangdaiscott@163.com>
Date: Fri, 25 Jun 2021 13:34:04 +0800
Subject: [PATCH] HW21-0499 表字典接口存在SQL注入漏洞,增加签名拦截器

---
 jeecg-boot/README.md                                                                                                       |   2 +-
 jeecg-boot/jeecg-boot-base/jeecg-boot-base-api/jeecg-system-cloud-api/src/main/java/org/jeecg/config/FeignConfig.java      |  85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/CommonConstant.java                |   2 ++
 jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/PathMatcherUtil.java                   |  84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthConfiguration.java |  13 ++++++-------
 jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthInterceptor.java   |  31 ++++++++++---------------------
 jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/util/HttpUtils.java                    | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
 jeecg-boot/jeecg-boot-starter/jeecg-boot-starter-cloud/src/main/java/org/jeecg/config/FeignConfig.java                     | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
 8 files changed, 370 insertions(+), 63 deletions(-)
 create mode 100644 jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/PathMatcherUtil.java

diff --git a/jeecg-boot/README.md b/jeecg-boot/README.md
index 6793652..3af418a 100644
--- a/jeecg-boot/README.md
+++ b/jeecg-boot/README.md
@@ -43,7 +43,7 @@ Jeecg-Boot 低代码开发平台
 
 - 常见问题:  [http://jeecg.com/doc/qa](http://jeecg.com/doc/qa)
 
-- QQ交流群 :  ④774126647、③816531124(满)、①284271917、②769925425
+- QQ交流群 :  ③816531124、①284271917、②769925425
 
 
 ## 专项文档
diff --git a/jeecg-boot/jeecg-boot-base/jeecg-boot-base-api/jeecg-system-cloud-api/src/main/java/org/jeecg/config/FeignConfig.java b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-api/jeecg-system-cloud-api/src/main/java/org/jeecg/config/FeignConfig.java
index 63207d0..a0572cd 100644
--- a/jeecg-boot/jeecg-boot-base/jeecg-boot-base-api/jeecg-system-cloud-api/src/main/java/org/jeecg/config/FeignConfig.java
+++ b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-api/jeecg-system-cloud-api/src/main/java/org/jeecg/config/FeignConfig.java
@@ -1,26 +1,44 @@
 package org.jeecg.config;
 
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.alibaba.fastjson.support.config.FastJsonConfig;
+import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
+import com.alibaba.fastjson.support.springfox.SwaggerJsonSerializer;
 import feign.Feign;
 import feign.Logger;
 import feign.RequestInterceptor;
+import feign.codec.Decoder;
 import feign.codec.Encoder;
 import feign.form.spring.SpringFormEncoder;
 import lombok.extern.slf4j.Slf4j;
 import org.jeecg.common.constant.CommonConstant;
+import org.jeecg.common.util.DateUtils;
+import org.jeecg.common.util.PathMatcherUtil;
+import org.jeecg.config.sign.interceptor.SignAuthConfiguration;
+import org.jeecg.config.sign.util.HttpUtils;
+import org.jeecg.config.sign.util.SignUtil;
 import org.springframework.beans.factory.ObjectFactory;
 import org.springframework.boot.autoconfigure.AutoConfigureBefore;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
 import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
 import org.springframework.cloud.openfeign.FeignAutoConfiguration;
+import org.springframework.cloud.openfeign.support.SpringDecoder;
 import org.springframework.cloud.openfeign.support.SpringEncoder;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Primary;
 import org.springframework.context.annotation.Scope;
+import org.springframework.http.MediaType;
 import org.springframework.web.context.request.RequestContextHolder;
 import org.springframework.web.context.request.ServletRequestAttributes;
-import javax.servlet.http.HttpServletRequest;
 
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.SortedMap;
 
 @ConditionalOnClass(Feign.class)
 @AutoConfigureBefore(FeignAutoConfiguration.class)
@@ -37,11 +55,36 @@ public class FeignConfig {
                 log.info("Feign request: {}", request.getRequestURI());
                 // 将token信息放入header中
                 String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
-                if(token==null){
+                if(token==null || "".equals(token)){
                     token = request.getParameter("token");
                 }
                 log.info("Feign request token: {}", token);
                 requestTemplate.header(CommonConstant.X_ACCESS_TOKEN, token);
+
+                //根据URL地址过滤请求 【字典表参数签名验证】
+                if (PathMatcherUtil.matches(Arrays.asList(SignAuthConfiguration.urlList),requestTemplate.path())) {
+                    try {
+                        log.info("============================ [begin] fegin api url ============================");
+                        log.info(requestTemplate.path());
+                        log.info(requestTemplate.method());
+                        String queryLine = requestTemplate.queryLine();
+                        if(queryLine!=null && queryLine.startsWith("?")){
+                            queryLine = queryLine.substring(1);
+                        }
+                        log.info(queryLine);
+                        if(requestTemplate.body()!=null){
+                            log.info(new String(requestTemplate.body()));
+                        }
+                        SortedMap<String, String> allParams = HttpUtils.getAllParams(requestTemplate.path(),queryLine,requestTemplate.body(),requestTemplate.method());
+                        String sign = SignUtil.getParamsSign(allParams);
+                        log.info(" Feign request params sign: {}",sign);
+                        log.info("============================ [end] fegin api url ============================");
+                        requestTemplate.header(CommonConstant.X_SIGN, sign);
+                        requestTemplate.header(CommonConstant.X_TIMESTAMP, DateUtils.getCurrentTimestamp().toString());
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                }
             }
         };
     }
@@ -72,4 +115,42 @@ public class FeignConfig {
     public Encoder multipartFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
         return new SpringFormEncoder(new SpringEncoder(messageConverters));
     }
+
+    // update-begin--Author:sunjianlei Date:20210604 for: 给 Feign 添加 FastJson 的解析支持 ----------
+    @Bean
+    public Encoder feignEncoder() {
+        return new SpringEncoder(feignHttpMessageConverter());
+    }
+
+    @Bean
+    public Decoder feignDecoder() {
+        return new SpringDecoder(feignHttpMessageConverter());
+    }
+
+    /**
+     * 设置解码器为fastjson
+     *
+     * @return
+     */
+    private ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
+        final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(this.getFastJsonConverter());
+        return () -> httpMessageConverters;
+    }
+
+    private FastJsonHttpMessageConverter getFastJsonConverter() {
+        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
+
+        List<MediaType> supportedMediaTypes = new ArrayList<>();
+        MediaType mediaTypeJson = MediaType.valueOf(MediaType.APPLICATION_JSON_VALUE);
+        supportedMediaTypes.add(mediaTypeJson);
+        converter.setSupportedMediaTypes(supportedMediaTypes);
+        FastJsonConfig config = new FastJsonConfig();
+        config.getSerializeConfig().put(JSON.class, new SwaggerJsonSerializer());
+        config.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);
+        converter.setFastJsonConfig(config);
+
+        return converter;
+    }
+    // update-end--Author:sunjianlei Date:20210604 for: 给 Feign 添加 FastJson 的解析支持 ----------
+
 }
diff --git a/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/CommonConstant.java b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/CommonConstant.java
index ce8d889..0175d0f 100644
--- a/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/CommonConstant.java
+++ b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/CommonConstant.java
@@ -290,6 +290,8 @@ public interface CommonConstant {
 
 
     public final static String X_ACCESS_TOKEN = "X-Access-Token";
+    public final static String X_SIGN = "X-Sign";
+    public final static String X_TIMESTAMP = "X-TIMESTAMP";
 
     /**
      * 多租户 请求头
diff --git a/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/PathMatcherUtil.java b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/PathMatcherUtil.java
new file mode 100644
index 0000000..0b813f4
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/PathMatcherUtil.java
@@ -0,0 +1,84 @@
+package org.jeecg.common.util;
+
+import org.springframework.util.AntPathMatcher;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * 使用Spring自身提供的地址匹配工具匹配URL
+ */
+public class PathMatcherUtil {
+
+    public static void main(String[] args) {
+        String url = "/sys/dict/loadDictOrderByValue/tree,s2,2";
+        String p = "/sys/dict/loadDictOrderByValue/*";
+
+        System.out.println(PathMatcherUtil.match(p,url));
+    }
+
+    /**
+     * 实际验证路径匹配权限
+     *
+     * @param matchPath 权限url
+     * @param path      访问路径
+     * @return 是否拥有权限
+     */
+    public static boolean match(String matchPath, String path) {
+        SpringAntMatcher springAntMatcher = new SpringAntMatcher(matchPath, true);
+        return springAntMatcher.matches(path);
+    }
+
+    /**
+     * 实际验证路径匹配权限
+     *
+     * @param list 权限url
+     * @param path 访问路径
+     * @return 是否拥有权限
+     */
+    public static boolean matches(Collection<String> list, String path) {
+        for (String s : list) {
+            SpringAntMatcher springAntMatcher = new SpringAntMatcher(s, true);
+            if (springAntMatcher.matches(path)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 地址表达式匹配工具
+     */
+    private static class SpringAntMatcher implements Matcher {
+        private final AntPathMatcher antMatcher;
+        private final String pattern;
+
+        private SpringAntMatcher(String pattern, boolean caseSensitive) {
+            this.pattern = pattern;
+            this.antMatcher = createMatcher(caseSensitive);
+        }
+
+        @Override
+        public boolean matches(String path) {
+            return this.antMatcher.match(this.pattern, path);
+        }
+
+        @Override
+        public Map<String, String> extractUriTemplateVariables(String path) {
+            return this.antMatcher.extractUriTemplateVariables(this.pattern, path);
+        }
+
+        private static AntPathMatcher createMatcher(boolean caseSensitive) {
+            AntPathMatcher matcher = new AntPathMatcher();
+            matcher.setTrimTokens(false);
+            matcher.setCaseSensitive(caseSensitive);
+            return matcher;
+        }
+    }
+
+    private interface Matcher {
+        boolean matches(String var1);
+
+        Map<String, String> extractUriTemplateVariables(String var1);
+    }
+}
diff --git a/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthConfiguration.java b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthConfiguration.java
index 0414af2..aa231b1 100644
--- a/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthConfiguration.java
+++ b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthConfiguration.java
@@ -6,11 +6,14 @@ import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
 /**
- * online 拦截器配置
+ * 签名 拦截器配置
  */
 @Configuration
 public class SignAuthConfiguration implements WebMvcConfigurer {
-
+    public static String[] urlList = new String[] {"/sys/dict/getDictItems/*", "/sys/dict/loadDict/*",
+            "/sys/dict/loadDictOrderByValue/*", "/sys/dict/loadDictItem/*", "/sys/dict/loadTreeData",
+            "/sys/api/queryTableDictItemsByCode", "/sys/api/queryFilterTableDictInfo", "/sys/api/queryTableDictByKeys",
+            "/sys/api/translateDictFromTable", "/sys/api/translateDictFromTableByKeys"};
     @Bean
     public SignAuthInterceptor signAuthInterceptor() {
         return new SignAuthInterceptor();
@@ -18,10 +21,6 @@ public class SignAuthConfiguration implements WebMvcConfigurer {
 
     @Override
     public void addInterceptors(InterceptorRegistry registry) {
-        String[] inculudes = new String[] {"/sys/dict/getDictItems/*", "/sys/dict/loadDict/*",
-            "/sys/dict/loadDictOrderByValue/*", "/sys/dict/loadDictItem/*", "/sys/dict/loadTreeData",
-            "/sys/api/queryTableDictItemsByCode", "/sys/api/queryFilterTableDictInfo", "/sys/api/queryTableDictByKeys",
-            "/sys/api/translateDictFromTable", "/sys/api/translateDictFromTableByKeys"};
-        registry.addInterceptor(signAuthInterceptor()).addPathPatterns(inculudes);
+        registry.addInterceptor(signAuthInterceptor()).addPathPatterns(urlList);
     }
 }
diff --git a/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthInterceptor.java b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthInterceptor.java
index 4dcfa2c..8ca0e9b 100644
--- a/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthInterceptor.java
+++ b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthInterceptor.java
@@ -1,22 +1,20 @@
 package org.jeecg.config.sign.interceptor;
 
 
-import java.io.PrintWriter;
-import java.util.SortedMap;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
+import com.alibaba.fastjson.JSON;
+import lombok.extern.slf4j.Slf4j;
 import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.constant.CommonConstant;
 import org.jeecg.common.util.DateUtils;
 import org.jeecg.config.sign.util.BodyReaderHttpServletRequestWrapper;
 import org.jeecg.config.sign.util.HttpUtils;
 import org.jeecg.config.sign.util.SignUtil;
 import org.springframework.web.servlet.HandlerInterceptor;
 
-import com.alibaba.fastjson.JSON;
-
-import lombok.extern.slf4j.Slf4j;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.PrintWriter;
+import java.util.SortedMap;
 
 /**
  * 签名拦截器
@@ -31,13 +29,13 @@ public class SignAuthInterceptor implements HandlerInterceptor {
 
     @Override
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
-        log.debug("request URI = " + request.getRequestURI());
+        log.info("request URI = " + request.getRequestURI());
         HttpServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(request);
         //获取全部参数(包括URL和body上的)
         SortedMap<String, String> allParams = HttpUtils.getAllParams(requestWrapper);
         //对参数进行签名验证
-        String headerSign = request.getHeader("X-Sign");
-        String timesTamp = request.getHeader("X-TIMESTAMP");
+        String headerSign = request.getHeader(CommonConstant.X_SIGN);
+        String timesTamp = request.getHeader(CommonConstant.X_TIMESTAMP);
 
         //1.校验时间有消息
         try {
@@ -60,15 +58,6 @@ public class SignAuthInterceptor implements HandlerInterceptor {
         } else {
             log.error("request URI = " + request.getRequestURI());
             log.error("Sign 签名校验失败!Header Sign : {}",headerSign);
-//            //打印日志参数
-//            Set<String> keySet = allParams.keySet();
-//            Iterator<String> paramIt = keySet.iterator();
-//            while(paramIt.hasNext()){
-//                String pkey = paramIt.next();
-//                String pval = allParams.get(pkey);
-//                log.error(" ["+pkey+":"+pval+"] ");
-//            }
-
             //校验失败返回前端
             response.setCharacterEncoding("UTF-8");
             response.setContentType("application/json; charset=utf-8");
diff --git a/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/util/HttpUtils.java b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/util/HttpUtils.java
index 9e51bae..5a56339 100644
--- a/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/util/HttpUtils.java
+++ b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/util/HttpUtils.java
@@ -1,5 +1,10 @@
 package org.jeecg.config.sign.util;
 
+import com.alibaba.fastjson.JSONObject;
+import org.jeecg.common.util.oConvertUtils;
+import org.springframework.http.HttpMethod;
+
+import javax.servlet.http.HttpServletRequest;
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
@@ -10,35 +15,28 @@ import java.util.Map;
 import java.util.SortedMap;
 import java.util.TreeMap;
 
-import javax.servlet.http.HttpServletRequest;
-
-import org.jeecg.common.util.oConvertUtils;
-import org.springframework.http.HttpMethod;
-
-import com.alibaba.fastjson.JSONObject;
-
 /**
  * http 工具类 获取请求中的参数
- * 
- * @author show
- * @date 14:23 2019/5/29
+ *
+ * @author jeecg
+ * @date 20210621
  */
 public class HttpUtils {
 
     /**
      * 将URL的参数和body参数合并
-     * 
-     * @author show
-     * @date 14:24 2019/5/29
+     *
+     * @author jeecg
+     * @date 20210621
      * @param request
      */
     public static SortedMap<String, String> getAllParams(HttpServletRequest request) throws IOException {
 
         SortedMap<String, String> result = new TreeMap<>();
         // 获取URL上最后带逗号的参数变量 sys/dict/getDictItems/sys_user,realname,username
-        String pathVariable = request.getRequestURI().substring(request.getRequestURI().lastIndexOf("/")+1);
-        if(pathVariable.contains(",")){
-            result.put(SignUtil.xPathVariable,pathVariable);
+        String pathVariable = request.getRequestURI().substring(request.getRequestURI().lastIndexOf("/") + 1);
+        if (pathVariable.contains(",")) {
+            result.put(SignUtil.xPathVariable, pathVariable);
         }
         // 获取URL上的参数
         Map<String, String> urlParams = getUrlParams(request);
@@ -60,10 +58,44 @@ public class HttpUtils {
     }
 
     /**
+     * 将URL的参数和body参数合并
+     *
+     * @author jeecg
+     * @date 20210621
+     * @param queryString
+     */
+    public static SortedMap<String, String> getAllParams(String url, String queryString, byte[] body, String method)
+        throws IOException {
+
+        SortedMap<String, String> result = new TreeMap<>();
+        // 获取URL上最后带逗号的参数变量 sys/dict/getDictItems/sys_user,realname,username
+        String pathVariable = url.substring(url.lastIndexOf("/") + 1);
+        if (pathVariable.contains(",")) {
+            result.put(SignUtil.xPathVariable, pathVariable);
+        }
+        // 获取URL上的参数
+        Map<String, String> urlParams = getUrlParams(queryString);
+        for (Map.Entry entry : urlParams.entrySet()) {
+            result.put((String)entry.getKey(), (String)entry.getValue());
+        }
+        Map<String, String> allRequestParam = new HashMap<>(16);
+        // get请求不需要拿body参数
+        if (!HttpMethod.GET.name().equals(method)) {
+            allRequestParam = getAllRequestParam(body);
+        }
+        // 将URL的参数和body参数进行合并
+        if (allRequestParam != null) {
+            for (Map.Entry entry : allRequestParam.entrySet()) {
+                result.put((String)entry.getKey(), (String)entry.getValue());
+            }
+        }
+        return result;
+    }
+
+    /**
      * 获取 Body 参数
-     * 
-     * @author show
-     * @date 15:04 2019/5/30
+     *
+     * @date 15:04 20210621
      * @param request
      */
     public static Map<String, String> getAllRequestParam(final HttpServletRequest request) throws IOException {
@@ -80,14 +112,28 @@ public class HttpUtils {
     }
 
     /**
+     * 获取 Body 参数
+     *
+     * @date 15:04 20210621
+     * @param body
+     */
+    public static Map<String, String> getAllRequestParam(final byte[] body) throws IOException {
+        if(body==null){
+            return null;
+        }
+        String wholeStr = new String(body);
+        // 转化成json对象
+        return JSONObject.parseObject(wholeStr.toString(), Map.class);
+    }
+
+    /**
      * 将URL请求参数转换成Map
-     * 
-     * @author show
+     *
      * @param request
      */
     public static Map<String, String> getUrlParams(HttpServletRequest request) {
         Map<String, String> result = new HashMap<>(16);
-        if(oConvertUtils.isEmpty(request.getQueryString())){
+        if (oConvertUtils.isEmpty(request.getQueryString())) {
             return result;
         }
         String param = "";
@@ -103,4 +149,28 @@ public class HttpUtils {
         }
         return result;
     }
+
+    /**
+     * 将URL请求参数转换成Map
+     * 
+     * @param queryString
+     */
+    public static Map<String, String> getUrlParams(String queryString) {
+        Map<String, String> result = new HashMap<>(16);
+        if (oConvertUtils.isEmpty(queryString)) {
+            return result;
+        }
+        String param = "";
+        try {
+            param = URLDecoder.decode(queryString, "utf-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        String[] params = param.split("&");
+        for (String s : params) {
+            int index = s.indexOf("=");
+            result.put(s.substring(0, index), s.substring(index + 1));
+        }
+        return result;
+    }
 }
\ No newline at end of file
diff --git a/jeecg-boot/jeecg-boot-starter/jeecg-boot-starter-cloud/src/main/java/org/jeecg/config/FeignConfig.java b/jeecg-boot/jeecg-boot-starter/jeecg-boot-starter-cloud/src/main/java/org/jeecg/config/FeignConfig.java
index f19ea5f..3cc2b47 100644
--- a/jeecg-boot/jeecg-boot-starter/jeecg-boot-starter-cloud/src/main/java/org/jeecg/config/FeignConfig.java
+++ b/jeecg-boot/jeecg-boot-starter/jeecg-boot-starter-cloud/src/main/java/org/jeecg/config/FeignConfig.java
@@ -1,28 +1,47 @@
 package org.jeecg.config;
 
-import feign.Feign;
-import feign.Logger;
-import feign.RequestInterceptor;
-import feign.codec.Encoder;
-import feign.form.spring.SpringFormEncoder;
-import lombok.extern.slf4j.Slf4j;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.SortedMap;
+
+import javax.servlet.http.HttpServletRequest;
+
 import org.jeecg.common.constant.CommonConstant;
-import org.jeecg.config.FeignConfig;
+import org.jeecg.common.util.DateUtils;
+import org.jeecg.common.util.PathMatcherUtil;
+import org.jeecg.config.sign.interceptor.SignAuthConfiguration;
+import org.jeecg.config.sign.util.HttpUtils;
+import org.jeecg.config.sign.util.SignUtil;
 import org.springframework.beans.factory.ObjectFactory;
 import org.springframework.boot.autoconfigure.AutoConfigureBefore;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
 import org.springframework.cloud.openfeign.FeignAutoConfiguration;
+import org.springframework.cloud.openfeign.support.SpringDecoder;
 import org.springframework.cloud.openfeign.support.SpringEncoder;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Primary;
 import org.springframework.context.annotation.Scope;
+import org.springframework.http.MediaType;
 import org.springframework.web.context.request.RequestContextHolder;
 import org.springframework.web.context.request.ServletRequestAttributes;
-import javax.servlet.http.HttpServletRequest;
 
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.alibaba.fastjson.support.config.FastJsonConfig;
+import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
+import com.alibaba.fastjson.support.springfox.SwaggerJsonSerializer;
+
+import feign.Feign;
+import feign.Logger;
+import feign.RequestInterceptor;
+import feign.codec.Decoder;
+import feign.codec.Encoder;
+import feign.form.spring.SpringFormEncoder;
+import lombok.extern.slf4j.Slf4j;
 
 @ConditionalOnClass(Feign.class)
 @AutoConfigureBefore(FeignAutoConfiguration.class)
@@ -39,11 +58,36 @@ public class FeignConfig {
                 log.info("Feign request: {}", request.getRequestURI());
                 // 将token信息放入header中
                 String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
-                if(token==null){
+                if(token==null || "".equals(token)){
                     token = request.getParameter("token");
                 }
                 log.info("Feign request token: {}", token);
                 requestTemplate.header(CommonConstant.X_ACCESS_TOKEN, token);
+
+                //根据URL地址过滤请求 【字典表参数签名验证】
+                if (PathMatcherUtil.matches(Arrays.asList(SignAuthConfiguration.urlList),requestTemplate.path())) {
+                    try {
+                        log.info("============================ [begin] fegin starter url ============================");
+                        log.info(requestTemplate.path());
+                        log.info(requestTemplate.method());
+                        String queryLine = requestTemplate.queryLine();
+                        if(queryLine!=null && queryLine.startsWith("?")){
+                            queryLine = queryLine.substring(1);
+                        }
+                        log.info(queryLine);
+                        if(requestTemplate.body()!=null){
+                            log.info(new String(requestTemplate.body()));
+                        }
+                        SortedMap<String, String> allParams = HttpUtils.getAllParams(requestTemplate.path(),queryLine,requestTemplate.body(),requestTemplate.method());
+                        String sign = SignUtil.getParamsSign(allParams);
+                        log.info(" Feign request params sign: {}",sign);
+                        log.info("============================ [end] fegin starter url ============================");
+                        requestTemplate.header(CommonConstant.X_SIGN, sign);
+                        requestTemplate.header(CommonConstant.X_TIMESTAMP, DateUtils.getCurrentTimestamp().toString());
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                }
             }
         };
     }
@@ -74,4 +118,42 @@ public class FeignConfig {
     public Encoder multipartFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
         return new SpringFormEncoder(new SpringEncoder(messageConverters));
     }
+
+    // update-begin--Author:sunjianlei Date:20210604 for: 给 Feign 添加 FastJson 的解析支持 ----------
+    @Bean
+    public Encoder feignEncoder() {
+        return new SpringEncoder(feignHttpMessageConverter());
+    }
+
+    @Bean
+    public Decoder feignDecoder() {
+        return new SpringDecoder(feignHttpMessageConverter());
+    }
+
+    /**
+     * 设置解码器为fastjson
+     *
+     * @return
+     */
+    private ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
+        final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(this.getFastJsonConverter());
+        return () -> httpMessageConverters;
+    }
+
+    private FastJsonHttpMessageConverter getFastJsonConverter() {
+        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
+
+        List<MediaType> supportedMediaTypes = new ArrayList<>();
+        MediaType mediaTypeJson = MediaType.valueOf(MediaType.APPLICATION_JSON_VALUE);
+        supportedMediaTypes.add(mediaTypeJson);
+        converter.setSupportedMediaTypes(supportedMediaTypes);
+        FastJsonConfig config = new FastJsonConfig();
+        config.getSerializeConfig().put(JSON.class, new SwaggerJsonSerializer());
+        config.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);
+        converter.setFastJsonConfig(config);
+
+        return converter;
+    }
+    // update-end--Author:sunjianlei Date:20210604 for: 给 Feign 添加 FastJson 的解析支持 ----------
+
 }
--
libgit2 0.22.2