Commit 242ce79393632bbffefa93c26128e36a7f729a77
1 parent
ef4a293f
HW21-0499 表字典接口存在SQL注入漏洞,增加签名拦截器
Showing
8 changed files
with
370 additions
and
63 deletions
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 | } |