Commit 242ce79393632bbffefa93c26128e36a7f729a77
1 parent
ef4a293f
HW21-0499 表字典接口存在SQL注入漏洞,增加签名拦截器
Showing
8 changed files
with
370 additions
and
63 deletions
jeecg-boot/README.md
jeecg-boot/jeecg-boot-base/jeecg-boot-base-api/jeecg-system-cloud-api/src/main/java/org/jeecg/config/FeignConfig.java
1 | 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 | 8 | import feign.Feign; |
4 | 9 | import feign.Logger; |
5 | 10 | import feign.RequestInterceptor; |
11 | +import feign.codec.Decoder; | |
6 | 12 | import feign.codec.Encoder; |
7 | 13 | import feign.form.spring.SpringFormEncoder; |
8 | 14 | import lombok.extern.slf4j.Slf4j; |
9 | 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 | 21 | import org.springframework.beans.factory.ObjectFactory; |
11 | 22 | import org.springframework.boot.autoconfigure.AutoConfigureBefore; |
12 | 23 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; |
13 | 24 | import org.springframework.boot.autoconfigure.http.HttpMessageConverters; |
14 | 25 | import org.springframework.cloud.openfeign.FeignAutoConfiguration; |
26 | +import org.springframework.cloud.openfeign.support.SpringDecoder; | |
15 | 27 | import org.springframework.cloud.openfeign.support.SpringEncoder; |
16 | 28 | import org.springframework.context.annotation.Bean; |
17 | 29 | import org.springframework.context.annotation.Configuration; |
18 | 30 | import org.springframework.context.annotation.Primary; |
19 | 31 | import org.springframework.context.annotation.Scope; |
32 | +import org.springframework.http.MediaType; | |
20 | 33 | import org.springframework.web.context.request.RequestContextHolder; |
21 | 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 | 43 | @ConditionalOnClass(Feign.class) |
26 | 44 | @AutoConfigureBefore(FeignAutoConfiguration.class) |
... | ... | @@ -37,11 +55,36 @@ public class FeignConfig { |
37 | 55 | log.info("Feign request: {}", request.getRequestURI()); |
38 | 56 | // 将token信息放入header中 |
39 | 57 | String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN); |
40 | - if(token==null){ | |
58 | + if(token==null || "".equals(token)){ | |
41 | 59 | token = request.getParameter("token"); |
42 | 60 | } |
43 | 61 | log.info("Feign request token: {}", token); |
44 | 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 | 115 | public Encoder multipartFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) { |
73 | 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 | 290 | |
291 | 291 | |
292 | 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 | 6 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
7 | 7 | |
8 | 8 | /** |
9 | - * online 拦截器配置 | |
9 | + * 签名 拦截器配置 | |
10 | 10 | */ |
11 | 11 | @Configuration |
12 | 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 | 17 | @Bean |
15 | 18 | public SignAuthInterceptor signAuthInterceptor() { |
16 | 19 | return new SignAuthInterceptor(); |
... | ... | @@ -18,10 +21,6 @@ public class SignAuthConfiguration implements WebMvcConfigurer { |
18 | 21 | |
19 | 22 | @Override |
20 | 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 | 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 | 6 | import org.jeecg.common.api.vo.Result; |
7 | +import org.jeecg.common.constant.CommonConstant; | |
11 | 8 | import org.jeecg.common.util.DateUtils; |
12 | 9 | import org.jeecg.config.sign.util.BodyReaderHttpServletRequestWrapper; |
13 | 10 | import org.jeecg.config.sign.util.HttpUtils; |
14 | 11 | import org.jeecg.config.sign.util.SignUtil; |
15 | 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 | 29 | |
32 | 30 | @Override |
33 | 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 | 33 | HttpServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(request); |
36 | 34 | //获取全部参数(包括URL和body上的) |
37 | 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 | 40 | //1.校验时间有消息 |
43 | 41 | try { |
... | ... | @@ -60,15 +58,6 @@ public class SignAuthInterceptor implements HandlerInterceptor { |
60 | 58 | } else { |
61 | 59 | log.error("request URI = " + request.getRequestURI()); |
62 | 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 | 62 | response.setCharacterEncoding("UTF-8"); |
74 | 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 | 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 | 8 | import java.io.BufferedReader; |
4 | 9 | import java.io.IOException; |
5 | 10 | import java.io.InputStreamReader; |
... | ... | @@ -10,35 +15,28 @@ import java.util.Map; |
10 | 15 | import java.util.SortedMap; |
11 | 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 | 19 | * http 工具类 获取请求中的参数 |
22 | - * | |
23 | - * @author show | |
24 | - * @date 14:23 2019/5/29 | |
20 | + * | |
21 | + * @author jeecg | |
22 | + * @date 20210621 | |
25 | 23 | */ |
26 | 24 | public class HttpUtils { |
27 | 25 | |
28 | 26 | /** |
29 | 27 | * 将URL的参数和body参数合并 |
30 | - * | |
31 | - * @author show | |
32 | - * @date 14:24 2019/5/29 | |
28 | + * | |
29 | + * @author jeecg | |
30 | + * @date 20210621 | |
33 | 31 | * @param request |
34 | 32 | */ |
35 | 33 | public static SortedMap<String, String> getAllParams(HttpServletRequest request) throws IOException { |
36 | 34 | |
37 | 35 | SortedMap<String, String> result = new TreeMap<>(); |
38 | 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 | 41 | // 获取URL上的参数 |
44 | 42 | Map<String, String> urlParams = getUrlParams(request); |
... | ... | @@ -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 | 96 | * 获取 Body 参数 |
64 | - * | |
65 | - * @author show | |
66 | - * @date 15:04 2019/5/30 | |
97 | + * | |
98 | + * @date 15:04 20210621 | |
67 | 99 | * @param request |
68 | 100 | */ |
69 | 101 | public static Map<String, String> getAllRequestParam(final HttpServletRequest request) throws IOException { |
... | ... | @@ -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 | 130 | * 将URL请求参数转换成Map |
84 | - * | |
85 | - * @author show | |
131 | + * | |
86 | 132 | * @param request |
87 | 133 | */ |
88 | 134 | public static Map<String, String> getUrlParams(HttpServletRequest request) { |
89 | 135 | Map<String, String> result = new HashMap<>(16); |
90 | - if(oConvertUtils.isEmpty(request.getQueryString())){ | |
136 | + if (oConvertUtils.isEmpty(request.getQueryString())) { | |
91 | 137 | return result; |
92 | 138 | } |
93 | 139 | String param = ""; |
... | ... | @@ -103,4 +149,28 @@ public class HttpUtils { |
103 | 149 | } |
104 | 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 | 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 | 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 | 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 | 17 | import org.springframework.beans.factory.ObjectFactory; |
12 | 18 | import org.springframework.boot.autoconfigure.AutoConfigureBefore; |
13 | 19 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; |
14 | -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | |
15 | 20 | import org.springframework.boot.autoconfigure.http.HttpMessageConverters; |
16 | 21 | import org.springframework.cloud.openfeign.FeignAutoConfiguration; |
22 | +import org.springframework.cloud.openfeign.support.SpringDecoder; | |
17 | 23 | import org.springframework.cloud.openfeign.support.SpringEncoder; |
18 | 24 | import org.springframework.context.annotation.Bean; |
19 | 25 | import org.springframework.context.annotation.Configuration; |
20 | 26 | import org.springframework.context.annotation.Primary; |
21 | 27 | import org.springframework.context.annotation.Scope; |
28 | +import org.springframework.http.MediaType; | |
22 | 29 | import org.springframework.web.context.request.RequestContextHolder; |
23 | 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 | 46 | @ConditionalOnClass(Feign.class) |
28 | 47 | @AutoConfigureBefore(FeignAutoConfiguration.class) |
... | ... | @@ -39,11 +58,36 @@ public class FeignConfig { |
39 | 58 | log.info("Feign request: {}", request.getRequestURI()); |
40 | 59 | // 将token信息放入header中 |
41 | 60 | String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN); |
42 | - if(token==null){ | |
61 | + if(token==null || "".equals(token)){ | |
43 | 62 | token = request.getParameter("token"); |
44 | 63 | } |
45 | 64 | log.info("Feign request token: {}", token); |
46 | 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 | 118 | public Encoder multipartFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) { |
75 | 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 | } |
... | ... |