Commit 242ce79393632bbffefa93c26128e36a7f729a77

Authored by zhangdaiscott
1 parent ef4a293f

HW21-0499 表字典接口存在SQL注入漏洞,增加签名拦截器

jeecg-boot/README.md
@@ -43,7 +43,7 @@ Jeecg-Boot 低代码开发平台 @@ -43,7 +43,7 @@ Jeecg-Boot 低代码开发平台
43 43
44 - 常见问题: [http://jeecg.com/doc/qa](http://jeecg.com/doc/qa) 44 - 常见问题: [http://jeecg.com/doc/qa](http://jeecg.com/doc/qa)
45 45
46 -- QQ交流群 : ④774126647、③816531124(满)、①284271917、②769925425 46 +- QQ交流群 : ③816531124、①284271917、②769925425
47 47
48 48
49 ## 专项文档 49 ## 专项文档
jeecg-boot/jeecg-boot-base/jeecg-boot-base-api/jeecg-system-cloud-api/src/main/java/org/jeecg/config/FeignConfig.java
1 package org.jeecg.config; 1 package org.jeecg.config;
2 2
  3 +import com.alibaba.fastjson.JSON;
  4 +import com.alibaba.fastjson.serializer.SerializerFeature;
  5 +import com.alibaba.fastjson.support.config.FastJsonConfig;
  6 +import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
  7 +import com.alibaba.fastjson.support.springfox.SwaggerJsonSerializer;
3 import feign.Feign; 8 import feign.Feign;
4 import feign.Logger; 9 import feign.Logger;
5 import feign.RequestInterceptor; 10 import feign.RequestInterceptor;
  11 +import feign.codec.Decoder;
6 import feign.codec.Encoder; 12 import feign.codec.Encoder;
7 import feign.form.spring.SpringFormEncoder; 13 import feign.form.spring.SpringFormEncoder;
8 import lombok.extern.slf4j.Slf4j; 14 import lombok.extern.slf4j.Slf4j;
9 import org.jeecg.common.constant.CommonConstant; 15 import org.jeecg.common.constant.CommonConstant;
  16 +import org.jeecg.common.util.DateUtils;
  17 +import org.jeecg.common.util.PathMatcherUtil;
  18 +import org.jeecg.config.sign.interceptor.SignAuthConfiguration;
  19 +import org.jeecg.config.sign.util.HttpUtils;
  20 +import org.jeecg.config.sign.util.SignUtil;
10 import org.springframework.beans.factory.ObjectFactory; 21 import org.springframework.beans.factory.ObjectFactory;
11 import org.springframework.boot.autoconfigure.AutoConfigureBefore; 22 import org.springframework.boot.autoconfigure.AutoConfigureBefore;
12 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 23 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
13 import org.springframework.boot.autoconfigure.http.HttpMessageConverters; 24 import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
14 import org.springframework.cloud.openfeign.FeignAutoConfiguration; 25 import org.springframework.cloud.openfeign.FeignAutoConfiguration;
  26 +import org.springframework.cloud.openfeign.support.SpringDecoder;
15 import org.springframework.cloud.openfeign.support.SpringEncoder; 27 import org.springframework.cloud.openfeign.support.SpringEncoder;
16 import org.springframework.context.annotation.Bean; 28 import org.springframework.context.annotation.Bean;
17 import org.springframework.context.annotation.Configuration; 29 import org.springframework.context.annotation.Configuration;
18 import org.springframework.context.annotation.Primary; 30 import org.springframework.context.annotation.Primary;
19 import org.springframework.context.annotation.Scope; 31 import org.springframework.context.annotation.Scope;
  32 +import org.springframework.http.MediaType;
20 import org.springframework.web.context.request.RequestContextHolder; 33 import org.springframework.web.context.request.RequestContextHolder;
21 import org.springframework.web.context.request.ServletRequestAttributes; 34 import org.springframework.web.context.request.ServletRequestAttributes;
22 -import javax.servlet.http.HttpServletRequest;  
23 35
  36 +import javax.servlet.http.HttpServletRequest;
  37 +import java.io.IOException;
  38 +import java.util.ArrayList;
  39 +import java.util.Arrays;
  40 +import java.util.List;
  41 +import java.util.SortedMap;
24 42
25 @ConditionalOnClass(Feign.class) 43 @ConditionalOnClass(Feign.class)
26 @AutoConfigureBefore(FeignAutoConfiguration.class) 44 @AutoConfigureBefore(FeignAutoConfiguration.class)
@@ -37,11 +55,36 @@ public class FeignConfig { @@ -37,11 +55,36 @@ public class FeignConfig {
37 log.info("Feign request: {}", request.getRequestURI()); 55 log.info("Feign request: {}", request.getRequestURI());
38 // 将token信息放入header中 56 // 将token信息放入header中
39 String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN); 57 String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
40 - if(token==null){ 58 + if(token==null || "".equals(token)){
41 token = request.getParameter("token"); 59 token = request.getParameter("token");
42 } 60 }
43 log.info("Feign request token: {}", token); 61 log.info("Feign request token: {}", token);
44 requestTemplate.header(CommonConstant.X_ACCESS_TOKEN, token); 62 requestTemplate.header(CommonConstant.X_ACCESS_TOKEN, token);
  63 +
  64 + //根据URL地址过滤请求 【字典表参数签名验证】
  65 + if (PathMatcherUtil.matches(Arrays.asList(SignAuthConfiguration.urlList),requestTemplate.path())) {
  66 + try {
  67 + log.info("============================ [begin] fegin api url ============================");
  68 + log.info(requestTemplate.path());
  69 + log.info(requestTemplate.method());
  70 + String queryLine = requestTemplate.queryLine();
  71 + if(queryLine!=null && queryLine.startsWith("?")){
  72 + queryLine = queryLine.substring(1);
  73 + }
  74 + log.info(queryLine);
  75 + if(requestTemplate.body()!=null){
  76 + log.info(new String(requestTemplate.body()));
  77 + }
  78 + SortedMap<String, String> allParams = HttpUtils.getAllParams(requestTemplate.path(),queryLine,requestTemplate.body(),requestTemplate.method());
  79 + String sign = SignUtil.getParamsSign(allParams);
  80 + log.info(" Feign request params sign: {}",sign);
  81 + log.info("============================ [end] fegin api url ============================");
  82 + requestTemplate.header(CommonConstant.X_SIGN, sign);
  83 + requestTemplate.header(CommonConstant.X_TIMESTAMP, DateUtils.getCurrentTimestamp().toString());
  84 + } catch (IOException e) {
  85 + e.printStackTrace();
  86 + }
  87 + }
45 } 88 }
46 }; 89 };
47 } 90 }
@@ -72,4 +115,42 @@ public class FeignConfig { @@ -72,4 +115,42 @@ public class FeignConfig {
72 public Encoder multipartFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) { 115 public Encoder multipartFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
73 return new SpringFormEncoder(new SpringEncoder(messageConverters)); 116 return new SpringFormEncoder(new SpringEncoder(messageConverters));
74 } 117 }
  118 +
  119 + // update-begin--Author:sunjianlei Date:20210604 for: 给 Feign 添加 FastJson 的解析支持 ----------
  120 + @Bean
  121 + public Encoder feignEncoder() {
  122 + return new SpringEncoder(feignHttpMessageConverter());
  123 + }
  124 +
  125 + @Bean
  126 + public Decoder feignDecoder() {
  127 + return new SpringDecoder(feignHttpMessageConverter());
  128 + }
  129 +
  130 + /**
  131 + * 设置解码器为fastjson
  132 + *
  133 + * @return
  134 + */
  135 + private ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
  136 + final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(this.getFastJsonConverter());
  137 + return () -> httpMessageConverters;
  138 + }
  139 +
  140 + private FastJsonHttpMessageConverter getFastJsonConverter() {
  141 + FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
  142 +
  143 + List<MediaType> supportedMediaTypes = new ArrayList<>();
  144 + MediaType mediaTypeJson = MediaType.valueOf(MediaType.APPLICATION_JSON_VALUE);
  145 + supportedMediaTypes.add(mediaTypeJson);
  146 + converter.setSupportedMediaTypes(supportedMediaTypes);
  147 + FastJsonConfig config = new FastJsonConfig();
  148 + config.getSerializeConfig().put(JSON.class, new SwaggerJsonSerializer());
  149 + config.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);
  150 + converter.setFastJsonConfig(config);
  151 +
  152 + return converter;
  153 + }
  154 + // update-end--Author:sunjianlei Date:20210604 for: 给 Feign 添加 FastJson 的解析支持 ----------
  155 +
75 } 156 }
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 { @@ -290,6 +290,8 @@ public interface CommonConstant {
290 290
291 291
292 public final static String X_ACCESS_TOKEN = "X-Access-Token"; 292 public final static String X_ACCESS_TOKEN = "X-Access-Token";
  293 + public final static String X_SIGN = "X-Sign";
  294 + public final static String X_TIMESTAMP = "X-TIMESTAMP";
293 295
294 /** 296 /**
295 * 多租户 请求头 297 * 多租户 请求头
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/PathMatcherUtil.java 0 → 100644
  1 +package org.jeecg.common.util;
  2 +
  3 +import org.springframework.util.AntPathMatcher;
  4 +
  5 +import java.util.Collection;
  6 +import java.util.Map;
  7 +
  8 +/**
  9 + * 使用Spring自身提供的地址匹配工具匹配URL
  10 + */
  11 +public class PathMatcherUtil {
  12 +
  13 + public static void main(String[] args) {
  14 + String url = "/sys/dict/loadDictOrderByValue/tree,s2,2";
  15 + String p = "/sys/dict/loadDictOrderByValue/*";
  16 +
  17 + System.out.println(PathMatcherUtil.match(p,url));
  18 + }
  19 +
  20 + /**
  21 + * 实际验证路径匹配权限
  22 + *
  23 + * @param matchPath 权限url
  24 + * @param path 访问路径
  25 + * @return 是否拥有权限
  26 + */
  27 + public static boolean match(String matchPath, String path) {
  28 + SpringAntMatcher springAntMatcher = new SpringAntMatcher(matchPath, true);
  29 + return springAntMatcher.matches(path);
  30 + }
  31 +
  32 + /**
  33 + * 实际验证路径匹配权限
  34 + *
  35 + * @param list 权限url
  36 + * @param path 访问路径
  37 + * @return 是否拥有权限
  38 + */
  39 + public static boolean matches(Collection<String> list, String path) {
  40 + for (String s : list) {
  41 + SpringAntMatcher springAntMatcher = new SpringAntMatcher(s, true);
  42 + if (springAntMatcher.matches(path)) {
  43 + return true;
  44 + }
  45 + }
  46 + return false;
  47 + }
  48 +
  49 + /**
  50 + * 地址表达式匹配工具
  51 + */
  52 + private static class SpringAntMatcher implements Matcher {
  53 + private final AntPathMatcher antMatcher;
  54 + private final String pattern;
  55 +
  56 + private SpringAntMatcher(String pattern, boolean caseSensitive) {
  57 + this.pattern = pattern;
  58 + this.antMatcher = createMatcher(caseSensitive);
  59 + }
  60 +
  61 + @Override
  62 + public boolean matches(String path) {
  63 + return this.antMatcher.match(this.pattern, path);
  64 + }
  65 +
  66 + @Override
  67 + public Map<String, String> extractUriTemplateVariables(String path) {
  68 + return this.antMatcher.extractUriTemplateVariables(this.pattern, path);
  69 + }
  70 +
  71 + private static AntPathMatcher createMatcher(boolean caseSensitive) {
  72 + AntPathMatcher matcher = new AntPathMatcher();
  73 + matcher.setTrimTokens(false);
  74 + matcher.setCaseSensitive(caseSensitive);
  75 + return matcher;
  76 + }
  77 + }
  78 +
  79 + private interface Matcher {
  80 + boolean matches(String var1);
  81 +
  82 + Map<String, String> extractUriTemplateVariables(String var1);
  83 + }
  84 +}
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; @@ -6,11 +6,14 @@ import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
6 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 6 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
7 7
8 /** 8 /**
9 - * online 拦截器配置 9 + * 签名 拦截器配置
10 */ 10 */
11 @Configuration 11 @Configuration
12 public class SignAuthConfiguration implements WebMvcConfigurer { 12 public class SignAuthConfiguration implements WebMvcConfigurer {
13 - 13 + public static String[] urlList = new String[] {"/sys/dict/getDictItems/*", "/sys/dict/loadDict/*",
  14 + "/sys/dict/loadDictOrderByValue/*", "/sys/dict/loadDictItem/*", "/sys/dict/loadTreeData",
  15 + "/sys/api/queryTableDictItemsByCode", "/sys/api/queryFilterTableDictInfo", "/sys/api/queryTableDictByKeys",
  16 + "/sys/api/translateDictFromTable", "/sys/api/translateDictFromTableByKeys"};
14 @Bean 17 @Bean
15 public SignAuthInterceptor signAuthInterceptor() { 18 public SignAuthInterceptor signAuthInterceptor() {
16 return new SignAuthInterceptor(); 19 return new SignAuthInterceptor();
@@ -18,10 +21,6 @@ public class SignAuthConfiguration implements WebMvcConfigurer { @@ -18,10 +21,6 @@ public class SignAuthConfiguration implements WebMvcConfigurer {
18 21
19 @Override 22 @Override
20 public void addInterceptors(InterceptorRegistry registry) { 23 public void addInterceptors(InterceptorRegistry registry) {
21 - String[] inculudes = new String[] {"/sys/dict/getDictItems/*", "/sys/dict/loadDict/*",  
22 - "/sys/dict/loadDictOrderByValue/*", "/sys/dict/loadDictItem/*", "/sys/dict/loadTreeData",  
23 - "/sys/api/queryTableDictItemsByCode", "/sys/api/queryFilterTableDictInfo", "/sys/api/queryTableDictByKeys",  
24 - "/sys/api/translateDictFromTable", "/sys/api/translateDictFromTableByKeys"};  
25 - registry.addInterceptor(signAuthInterceptor()).addPathPatterns(inculudes); 24 + registry.addInterceptor(signAuthInterceptor()).addPathPatterns(urlList);
26 } 25 }
27 } 26 }
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthInterceptor.java
1 package org.jeecg.config.sign.interceptor; 1 package org.jeecg.config.sign.interceptor;
2 2
3 3
4 -import java.io.PrintWriter;  
5 -import java.util.SortedMap;  
6 -  
7 -import javax.servlet.http.HttpServletRequest;  
8 -import javax.servlet.http.HttpServletResponse;  
9 - 4 +import com.alibaba.fastjson.JSON;
  5 +import lombok.extern.slf4j.Slf4j;
10 import org.jeecg.common.api.vo.Result; 6 import org.jeecg.common.api.vo.Result;
  7 +import org.jeecg.common.constant.CommonConstant;
11 import org.jeecg.common.util.DateUtils; 8 import org.jeecg.common.util.DateUtils;
12 import org.jeecg.config.sign.util.BodyReaderHttpServletRequestWrapper; 9 import org.jeecg.config.sign.util.BodyReaderHttpServletRequestWrapper;
13 import org.jeecg.config.sign.util.HttpUtils; 10 import org.jeecg.config.sign.util.HttpUtils;
14 import org.jeecg.config.sign.util.SignUtil; 11 import org.jeecg.config.sign.util.SignUtil;
15 import org.springframework.web.servlet.HandlerInterceptor; 12 import org.springframework.web.servlet.HandlerInterceptor;
16 13
17 -import com.alibaba.fastjson.JSON;  
18 -  
19 -import lombok.extern.slf4j.Slf4j; 14 +import javax.servlet.http.HttpServletRequest;
  15 +import javax.servlet.http.HttpServletResponse;
  16 +import java.io.PrintWriter;
  17 +import java.util.SortedMap;
20 18
21 /** 19 /**
22 * 签名拦截器 20 * 签名拦截器
@@ -31,13 +29,13 @@ public class SignAuthInterceptor implements HandlerInterceptor { @@ -31,13 +29,13 @@ public class SignAuthInterceptor implements HandlerInterceptor {
31 29
32 @Override 30 @Override
33 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 31 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
34 - log.debug("request URI = " + request.getRequestURI()); 32 + log.info("request URI = " + request.getRequestURI());
35 HttpServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(request); 33 HttpServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(request);
36 //获取全部参数(包括URL和body上的) 34 //获取全部参数(包括URL和body上的)
37 SortedMap<String, String> allParams = HttpUtils.getAllParams(requestWrapper); 35 SortedMap<String, String> allParams = HttpUtils.getAllParams(requestWrapper);
38 //对参数进行签名验证 36 //对参数进行签名验证
39 - String headerSign = request.getHeader("X-Sign");  
40 - String timesTamp = request.getHeader("X-TIMESTAMP"); 37 + String headerSign = request.getHeader(CommonConstant.X_SIGN);
  38 + String timesTamp = request.getHeader(CommonConstant.X_TIMESTAMP);
41 39
42 //1.校验时间有消息 40 //1.校验时间有消息
43 try { 41 try {
@@ -60,15 +58,6 @@ public class SignAuthInterceptor implements HandlerInterceptor { @@ -60,15 +58,6 @@ public class SignAuthInterceptor implements HandlerInterceptor {
60 } else { 58 } else {
61 log.error("request URI = " + request.getRequestURI()); 59 log.error("request URI = " + request.getRequestURI());
62 log.error("Sign 签名校验失败!Header Sign : {}",headerSign); 60 log.error("Sign 签名校验失败!Header Sign : {}",headerSign);
63 -// //打印日志参数  
64 -// Set<String> keySet = allParams.keySet();  
65 -// Iterator<String> paramIt = keySet.iterator();  
66 -// while(paramIt.hasNext()){  
67 -// String pkey = paramIt.next();  
68 -// String pval = allParams.get(pkey);  
69 -// log.error(" ["+pkey+":"+pval+"] ");  
70 -// }  
71 -  
72 //校验失败返回前端 61 //校验失败返回前端
73 response.setCharacterEncoding("UTF-8"); 62 response.setCharacterEncoding("UTF-8");
74 response.setContentType("application/json; charset=utf-8"); 63 response.setContentType("application/json; charset=utf-8");
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/util/HttpUtils.java
1 package org.jeecg.config.sign.util; 1 package org.jeecg.config.sign.util;
2 2
  3 +import com.alibaba.fastjson.JSONObject;
  4 +import org.jeecg.common.util.oConvertUtils;
  5 +import org.springframework.http.HttpMethod;
  6 +
  7 +import javax.servlet.http.HttpServletRequest;
3 import java.io.BufferedReader; 8 import java.io.BufferedReader;
4 import java.io.IOException; 9 import java.io.IOException;
5 import java.io.InputStreamReader; 10 import java.io.InputStreamReader;
@@ -10,35 +15,28 @@ import java.util.Map; @@ -10,35 +15,28 @@ import java.util.Map;
10 import java.util.SortedMap; 15 import java.util.SortedMap;
11 import java.util.TreeMap; 16 import java.util.TreeMap;
12 17
13 -import javax.servlet.http.HttpServletRequest;  
14 -  
15 -import org.jeecg.common.util.oConvertUtils;  
16 -import org.springframework.http.HttpMethod;  
17 -  
18 -import com.alibaba.fastjson.JSONObject;  
19 -  
20 /** 18 /**
21 * http 工具类 获取请求中的参数 19 * http 工具类 获取请求中的参数
22 - *  
23 - * @author show  
24 - * @date 14:23 2019/5/29 20 + *
  21 + * @author jeecg
  22 + * @date 20210621
25 */ 23 */
26 public class HttpUtils { 24 public class HttpUtils {
27 25
28 /** 26 /**
29 * 将URL的参数和body参数合并 27 * 将URL的参数和body参数合并
30 - *  
31 - * @author show  
32 - * @date 14:24 2019/5/29 28 + *
  29 + * @author jeecg
  30 + * @date 20210621
33 * @param request 31 * @param request
34 */ 32 */
35 public static SortedMap<String, String> getAllParams(HttpServletRequest request) throws IOException { 33 public static SortedMap<String, String> getAllParams(HttpServletRequest request) throws IOException {
36 34
37 SortedMap<String, String> result = new TreeMap<>(); 35 SortedMap<String, String> result = new TreeMap<>();
38 // 获取URL上最后带逗号的参数变量 sys/dict/getDictItems/sys_user,realname,username 36 // 获取URL上最后带逗号的参数变量 sys/dict/getDictItems/sys_user,realname,username
39 - String pathVariable = request.getRequestURI().substring(request.getRequestURI().lastIndexOf("/")+1);  
40 - if(pathVariable.contains(",")){  
41 - result.put(SignUtil.xPathVariable,pathVariable); 37 + String pathVariable = request.getRequestURI().substring(request.getRequestURI().lastIndexOf("/") + 1);
  38 + if (pathVariable.contains(",")) {
  39 + result.put(SignUtil.xPathVariable, pathVariable);
42 } 40 }
43 // 获取URL上的参数 41 // 获取URL上的参数
44 Map<String, String> urlParams = getUrlParams(request); 42 Map<String, String> urlParams = getUrlParams(request);
@@ -60,10 +58,44 @@ public class HttpUtils { @@ -60,10 +58,44 @@ public class HttpUtils {
60 } 58 }
61 59
62 /** 60 /**
  61 + * 将URL的参数和body参数合并
  62 + *
  63 + * @author jeecg
  64 + * @date 20210621
  65 + * @param queryString
  66 + */
  67 + public static SortedMap<String, String> getAllParams(String url, String queryString, byte[] body, String method)
  68 + throws IOException {
  69 +
  70 + SortedMap<String, String> result = new TreeMap<>();
  71 + // 获取URL上最后带逗号的参数变量 sys/dict/getDictItems/sys_user,realname,username
  72 + String pathVariable = url.substring(url.lastIndexOf("/") + 1);
  73 + if (pathVariable.contains(",")) {
  74 + result.put(SignUtil.xPathVariable, pathVariable);
  75 + }
  76 + // 获取URL上的参数
  77 + Map<String, String> urlParams = getUrlParams(queryString);
  78 + for (Map.Entry entry : urlParams.entrySet()) {
  79 + result.put((String)entry.getKey(), (String)entry.getValue());
  80 + }
  81 + Map<String, String> allRequestParam = new HashMap<>(16);
  82 + // get请求不需要拿body参数
  83 + if (!HttpMethod.GET.name().equals(method)) {
  84 + allRequestParam = getAllRequestParam(body);
  85 + }
  86 + // 将URL的参数和body参数进行合并
  87 + if (allRequestParam != null) {
  88 + for (Map.Entry entry : allRequestParam.entrySet()) {
  89 + result.put((String)entry.getKey(), (String)entry.getValue());
  90 + }
  91 + }
  92 + return result;
  93 + }
  94 +
  95 + /**
63 * 获取 Body 参数 96 * 获取 Body 参数
64 - *  
65 - * @author show  
66 - * @date 15:04 2019/5/30 97 + *
  98 + * @date 15:04 20210621
67 * @param request 99 * @param request
68 */ 100 */
69 public static Map<String, String> getAllRequestParam(final HttpServletRequest request) throws IOException { 101 public static Map<String, String> getAllRequestParam(final HttpServletRequest request) throws IOException {
@@ -80,14 +112,28 @@ public class HttpUtils { @@ -80,14 +112,28 @@ public class HttpUtils {
80 } 112 }
81 113
82 /** 114 /**
  115 + * 获取 Body 参数
  116 + *
  117 + * @date 15:04 20210621
  118 + * @param body
  119 + */
  120 + public static Map<String, String> getAllRequestParam(final byte[] body) throws IOException {
  121 + if(body==null){
  122 + return null;
  123 + }
  124 + String wholeStr = new String(body);
  125 + // 转化成json对象
  126 + return JSONObject.parseObject(wholeStr.toString(), Map.class);
  127 + }
  128 +
  129 + /**
83 * 将URL请求参数转换成Map 130 * 将URL请求参数转换成Map
84 - *  
85 - * @author show 131 + *
86 * @param request 132 * @param request
87 */ 133 */
88 public static Map<String, String> getUrlParams(HttpServletRequest request) { 134 public static Map<String, String> getUrlParams(HttpServletRequest request) {
89 Map<String, String> result = new HashMap<>(16); 135 Map<String, String> result = new HashMap<>(16);
90 - if(oConvertUtils.isEmpty(request.getQueryString())){ 136 + if (oConvertUtils.isEmpty(request.getQueryString())) {
91 return result; 137 return result;
92 } 138 }
93 String param = ""; 139 String param = "";
@@ -103,4 +149,28 @@ public class HttpUtils { @@ -103,4 +149,28 @@ public class HttpUtils {
103 } 149 }
104 return result; 150 return result;
105 } 151 }
  152 +
  153 + /**
  154 + * 将URL请求参数转换成Map
  155 + *
  156 + * @param queryString
  157 + */
  158 + public static Map<String, String> getUrlParams(String queryString) {
  159 + Map<String, String> result = new HashMap<>(16);
  160 + if (oConvertUtils.isEmpty(queryString)) {
  161 + return result;
  162 + }
  163 + String param = "";
  164 + try {
  165 + param = URLDecoder.decode(queryString, "utf-8");
  166 + } catch (UnsupportedEncodingException e) {
  167 + e.printStackTrace();
  168 + }
  169 + String[] params = param.split("&");
  170 + for (String s : params) {
  171 + int index = s.indexOf("=");
  172 + result.put(s.substring(0, index), s.substring(index + 1));
  173 + }
  174 + return result;
  175 + }
106 } 176 }
107 \ No newline at end of file 177 \ No newline at end of file
jeecg-boot/jeecg-boot-starter/jeecg-boot-starter-cloud/src/main/java/org/jeecg/config/FeignConfig.java
1 package org.jeecg.config; 1 package org.jeecg.config;
2 2
3 -import feign.Feign;  
4 -import feign.Logger;  
5 -import feign.RequestInterceptor;  
6 -import feign.codec.Encoder;  
7 -import feign.form.spring.SpringFormEncoder;  
8 -import lombok.extern.slf4j.Slf4j; 3 +import java.io.IOException;
  4 +import java.util.ArrayList;
  5 +import java.util.Arrays;
  6 +import java.util.List;
  7 +import java.util.SortedMap;
  8 +
  9 +import javax.servlet.http.HttpServletRequest;
  10 +
9 import org.jeecg.common.constant.CommonConstant; 11 import org.jeecg.common.constant.CommonConstant;
10 -import org.jeecg.config.FeignConfig; 12 +import org.jeecg.common.util.DateUtils;
  13 +import org.jeecg.common.util.PathMatcherUtil;
  14 +import org.jeecg.config.sign.interceptor.SignAuthConfiguration;
  15 +import org.jeecg.config.sign.util.HttpUtils;
  16 +import org.jeecg.config.sign.util.SignUtil;
11 import org.springframework.beans.factory.ObjectFactory; 17 import org.springframework.beans.factory.ObjectFactory;
12 import org.springframework.boot.autoconfigure.AutoConfigureBefore; 18 import org.springframework.boot.autoconfigure.AutoConfigureBefore;
13 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 19 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
14 -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;  
15 import org.springframework.boot.autoconfigure.http.HttpMessageConverters; 20 import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
16 import org.springframework.cloud.openfeign.FeignAutoConfiguration; 21 import org.springframework.cloud.openfeign.FeignAutoConfiguration;
  22 +import org.springframework.cloud.openfeign.support.SpringDecoder;
17 import org.springframework.cloud.openfeign.support.SpringEncoder; 23 import org.springframework.cloud.openfeign.support.SpringEncoder;
18 import org.springframework.context.annotation.Bean; 24 import org.springframework.context.annotation.Bean;
19 import org.springframework.context.annotation.Configuration; 25 import org.springframework.context.annotation.Configuration;
20 import org.springframework.context.annotation.Primary; 26 import org.springframework.context.annotation.Primary;
21 import org.springframework.context.annotation.Scope; 27 import org.springframework.context.annotation.Scope;
  28 +import org.springframework.http.MediaType;
22 import org.springframework.web.context.request.RequestContextHolder; 29 import org.springframework.web.context.request.RequestContextHolder;
23 import org.springframework.web.context.request.ServletRequestAttributes; 30 import org.springframework.web.context.request.ServletRequestAttributes;
24 -import javax.servlet.http.HttpServletRequest;  
25 31
  32 +import com.alibaba.fastjson.JSON;
  33 +import com.alibaba.fastjson.serializer.SerializerFeature;
  34 +import com.alibaba.fastjson.support.config.FastJsonConfig;
  35 +import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
  36 +import com.alibaba.fastjson.support.springfox.SwaggerJsonSerializer;
  37 +
  38 +import feign.Feign;
  39 +import feign.Logger;
  40 +import feign.RequestInterceptor;
  41 +import feign.codec.Decoder;
  42 +import feign.codec.Encoder;
  43 +import feign.form.spring.SpringFormEncoder;
  44 +import lombok.extern.slf4j.Slf4j;
26 45
27 @ConditionalOnClass(Feign.class) 46 @ConditionalOnClass(Feign.class)
28 @AutoConfigureBefore(FeignAutoConfiguration.class) 47 @AutoConfigureBefore(FeignAutoConfiguration.class)
@@ -39,11 +58,36 @@ public class FeignConfig { @@ -39,11 +58,36 @@ public class FeignConfig {
39 log.info("Feign request: {}", request.getRequestURI()); 58 log.info("Feign request: {}", request.getRequestURI());
40 // 将token信息放入header中 59 // 将token信息放入header中
41 String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN); 60 String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
42 - if(token==null){ 61 + if(token==null || "".equals(token)){
43 token = request.getParameter("token"); 62 token = request.getParameter("token");
44 } 63 }
45 log.info("Feign request token: {}", token); 64 log.info("Feign request token: {}", token);
46 requestTemplate.header(CommonConstant.X_ACCESS_TOKEN, token); 65 requestTemplate.header(CommonConstant.X_ACCESS_TOKEN, token);
  66 +
  67 + //根据URL地址过滤请求 【字典表参数签名验证】
  68 + if (PathMatcherUtil.matches(Arrays.asList(SignAuthConfiguration.urlList),requestTemplate.path())) {
  69 + try {
  70 + log.info("============================ [begin] fegin starter url ============================");
  71 + log.info(requestTemplate.path());
  72 + log.info(requestTemplate.method());
  73 + String queryLine = requestTemplate.queryLine();
  74 + if(queryLine!=null && queryLine.startsWith("?")){
  75 + queryLine = queryLine.substring(1);
  76 + }
  77 + log.info(queryLine);
  78 + if(requestTemplate.body()!=null){
  79 + log.info(new String(requestTemplate.body()));
  80 + }
  81 + SortedMap<String, String> allParams = HttpUtils.getAllParams(requestTemplate.path(),queryLine,requestTemplate.body(),requestTemplate.method());
  82 + String sign = SignUtil.getParamsSign(allParams);
  83 + log.info(" Feign request params sign: {}",sign);
  84 + log.info("============================ [end] fegin starter url ============================");
  85 + requestTemplate.header(CommonConstant.X_SIGN, sign);
  86 + requestTemplate.header(CommonConstant.X_TIMESTAMP, DateUtils.getCurrentTimestamp().toString());
  87 + } catch (IOException e) {
  88 + e.printStackTrace();
  89 + }
  90 + }
47 } 91 }
48 }; 92 };
49 } 93 }
@@ -74,4 +118,42 @@ public class FeignConfig { @@ -74,4 +118,42 @@ public class FeignConfig {
74 public Encoder multipartFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) { 118 public Encoder multipartFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
75 return new SpringFormEncoder(new SpringEncoder(messageConverters)); 119 return new SpringFormEncoder(new SpringEncoder(messageConverters));
76 } 120 }
  121 +
  122 + // update-begin--Author:sunjianlei Date:20210604 for: 给 Feign 添加 FastJson 的解析支持 ----------
  123 + @Bean
  124 + public Encoder feignEncoder() {
  125 + return new SpringEncoder(feignHttpMessageConverter());
  126 + }
  127 +
  128 + @Bean
  129 + public Decoder feignDecoder() {
  130 + return new SpringDecoder(feignHttpMessageConverter());
  131 + }
  132 +
  133 + /**
  134 + * 设置解码器为fastjson
  135 + *
  136 + * @return
  137 + */
  138 + private ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
  139 + final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(this.getFastJsonConverter());
  140 + return () -> httpMessageConverters;
  141 + }
  142 +
  143 + private FastJsonHttpMessageConverter getFastJsonConverter() {
  144 + FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
  145 +
  146 + List<MediaType> supportedMediaTypes = new ArrayList<>();
  147 + MediaType mediaTypeJson = MediaType.valueOf(MediaType.APPLICATION_JSON_VALUE);
  148 + supportedMediaTypes.add(mediaTypeJson);
  149 + converter.setSupportedMediaTypes(supportedMediaTypes);
  150 + FastJsonConfig config = new FastJsonConfig();
  151 + config.getSerializeConfig().put(JSON.class, new SwaggerJsonSerializer());
  152 + config.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);
  153 + converter.setFastJsonConfig(config);
  154 +
  155 + return converter;
  156 + }
  157 + // update-end--Author:sunjianlei Date:20210604 for: 给 Feign 添加 FastJson 的解析支持 ----------
  158 +
77 } 159 }