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

pull/2730/head
zhangdaiscott 3 years ago
parent 8b3d83ae0b
commit b1958fd295

@ -1,26 +1,44 @@
package org.jeecg.config;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.alibaba.fastjson.support.springfox.SwaggerJsonSerializer;
import feign.Feign;
import feign.Logger;
import feign.RequestInterceptor;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.PathMatcherUtil;
import org.jeecg.config.sign.interceptor.SignAuthConfiguration;
import org.jeecg.config.sign.util.HttpUtils;
import org.jeecg.config.sign.util.SignUtil;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.http.MediaType;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.SortedMap;
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
@ -37,11 +55,36 @@ public class FeignConfig {
log.info("Feign request: {}", request.getRequestURI());
// 将token信息放入header中
String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
if(token==null){
if(token==null || "".equals(token)){
token = request.getParameter("token");
}
log.info("Feign request token: {}", token);
requestTemplate.header(CommonConstant.X_ACCESS_TOKEN, token);
//根据URL地址过滤请求 【字典表参数签名验证】
if (PathMatcherUtil.matches(Arrays.asList(SignAuthConfiguration.urlList),requestTemplate.path())) {
try {
log.info("============================ [begin] fegin api url ============================");
log.info(requestTemplate.path());
log.info(requestTemplate.method());
String queryLine = requestTemplate.queryLine();
if(queryLine!=null && queryLine.startsWith("?")){
queryLine = queryLine.substring(1);
}
log.info(queryLine);
if(requestTemplate.body()!=null){
log.info(new String(requestTemplate.body()));
}
SortedMap<String, String> allParams = HttpUtils.getAllParams(requestTemplate.path(),queryLine,requestTemplate.body(),requestTemplate.method());
String sign = SignUtil.getParamsSign(allParams);
log.info(" Feign request params sign: {}",sign);
log.info("============================ [end] fegin api url ============================");
requestTemplate.header(CommonConstant.X_SIGN, sign);
requestTemplate.header(CommonConstant.X_TIMESTAMP, DateUtils.getCurrentTimestamp().toString());
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
}
@ -72,4 +115,42 @@ public class FeignConfig {
public Encoder multipartFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
return new SpringFormEncoder(new SpringEncoder(messageConverters));
}
// update-begin--Author:sunjianlei Date:20210604 for 给 Feign 添加 FastJson 的解析支持 ----------
@Bean
public Encoder feignEncoder() {
return new SpringEncoder(feignHttpMessageConverter());
}
@Bean
public Decoder feignDecoder() {
return new SpringDecoder(feignHttpMessageConverter());
}
/**
* fastjson
*
* @return
*/
private ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(this.getFastJsonConverter());
return () -> httpMessageConverters;
}
private FastJsonHttpMessageConverter getFastJsonConverter() {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
List<MediaType> supportedMediaTypes = new ArrayList<>();
MediaType mediaTypeJson = MediaType.valueOf(MediaType.APPLICATION_JSON_VALUE);
supportedMediaTypes.add(mediaTypeJson);
converter.setSupportedMediaTypes(supportedMediaTypes);
FastJsonConfig config = new FastJsonConfig();
config.getSerializeConfig().put(JSON.class, new SwaggerJsonSerializer());
config.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);
converter.setFastJsonConfig(config);
return converter;
}
// update-end--Author:sunjianlei Date:20210604 for 给 Feign 添加 FastJson 的解析支持 ----------
}

@ -290,6 +290,8 @@ public interface CommonConstant {
public final static String X_ACCESS_TOKEN = "X-Access-Token";
public final static String X_SIGN = "X-Sign";
public final static String X_TIMESTAMP = "X-TIMESTAMP";
/**
*

@ -0,0 +1,84 @@
package org.jeecg.common.util;
import org.springframework.util.AntPathMatcher;
import java.util.Collection;
import java.util.Map;
/**
* 使SpringURL
*/
public class PathMatcherUtil {
public static void main(String[] args) {
String url = "/sys/dict/loadDictOrderByValue/tree,s2,2";
String p = "/sys/dict/loadDictOrderByValue/*";
System.out.println(PathMatcherUtil.match(p,url));
}
/**
*
*
* @param matchPath url
* @param path 访
* @return
*/
public static boolean match(String matchPath, String path) {
SpringAntMatcher springAntMatcher = new SpringAntMatcher(matchPath, true);
return springAntMatcher.matches(path);
}
/**
*
*
* @param list url
* @param path 访
* @return
*/
public static boolean matches(Collection<String> list, String path) {
for (String s : list) {
SpringAntMatcher springAntMatcher = new SpringAntMatcher(s, true);
if (springAntMatcher.matches(path)) {
return true;
}
}
return false;
}
/**
*
*/
private static class SpringAntMatcher implements Matcher {
private final AntPathMatcher antMatcher;
private final String pattern;
private SpringAntMatcher(String pattern, boolean caseSensitive) {
this.pattern = pattern;
this.antMatcher = createMatcher(caseSensitive);
}
@Override
public boolean matches(String path) {
return this.antMatcher.match(this.pattern, path);
}
@Override
public Map<String, String> extractUriTemplateVariables(String path) {
return this.antMatcher.extractUriTemplateVariables(this.pattern, path);
}
private static AntPathMatcher createMatcher(boolean caseSensitive) {
AntPathMatcher matcher = new AntPathMatcher();
matcher.setTrimTokens(false);
matcher.setCaseSensitive(caseSensitive);
return matcher;
}
}
private interface Matcher {
boolean matches(String var1);
Map<String, String> extractUriTemplateVariables(String var1);
}
}

@ -6,11 +6,14 @@ import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* online
*
*/
@Configuration
public class SignAuthConfiguration implements WebMvcConfigurer {
public static String[] urlList = new String[] {"/sys/dict/getDictItems/*", "/sys/dict/loadDict/*",
"/sys/dict/loadDictOrderByValue/*", "/sys/dict/loadDictItem/*", "/sys/dict/loadTreeData",
"/sys/api/queryTableDictItemsByCode", "/sys/api/queryFilterTableDictInfo", "/sys/api/queryTableDictByKeys",
"/sys/api/translateDictFromTable", "/sys/api/translateDictFromTableByKeys"};
@Bean
public SignAuthInterceptor signAuthInterceptor() {
return new SignAuthInterceptor();
@ -18,10 +21,6 @@ public class SignAuthConfiguration implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
String[] inculudes = new String[] {"/sys/dict/getDictItems/*", "/sys/dict/loadDict/*",
"/sys/dict/loadDictOrderByValue/*", "/sys/dict/loadDictItem/*", "/sys/dict/loadTreeData",
"/sys/api/queryTableDictItemsByCode", "/sys/api/queryFilterTableDictInfo", "/sys/api/queryTableDictByKeys",
"/sys/api/translateDictFromTable", "/sys/api/translateDictFromTableByKeys"};
registry.addInterceptor(signAuthInterceptor()).addPathPatterns(inculudes);
registry.addInterceptor(signAuthInterceptor()).addPathPatterns(urlList);
}
}

@ -1,22 +1,20 @@
package org.jeecg.config.sign.interceptor;
import java.io.PrintWriter;
import java.util.SortedMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.util.DateUtils;
import org.jeecg.config.sign.util.BodyReaderHttpServletRequestWrapper;
import org.jeecg.config.sign.util.HttpUtils;
import org.jeecg.config.sign.util.SignUtil;
import org.springframework.web.servlet.HandlerInterceptor;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.SortedMap;
/**
*
@ -31,13 +29,13 @@ public class SignAuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.debug("request URI = " + request.getRequestURI());
log.info("request URI = " + request.getRequestURI());
HttpServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(request);
//获取全部参数(包括URL和body上的)
SortedMap<String, String> allParams = HttpUtils.getAllParams(requestWrapper);
//对参数进行签名验证
String headerSign = request.getHeader("X-Sign");
String timesTamp = request.getHeader("X-TIMESTAMP");
String headerSign = request.getHeader(CommonConstant.X_SIGN);
String timesTamp = request.getHeader(CommonConstant.X_TIMESTAMP);
//1.校验时间有消息
try {
@ -60,15 +58,6 @@ public class SignAuthInterceptor implements HandlerInterceptor {
} else {
log.error("request URI = " + request.getRequestURI());
log.error("Sign 签名校验失败Header Sign : {}",headerSign);
// //打印日志参数
// Set<String> keySet = allParams.keySet();
// Iterator<String> paramIt = keySet.iterator();
// while(paramIt.hasNext()){
// String pkey = paramIt.next();
// String pval = allParams.get(pkey);
// log.error(" ["+pkey+":"+pval+"] ");
// }
//校验失败返回前端
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");

@ -1,5 +1,10 @@
package org.jeecg.config.sign.util;
import com.alibaba.fastjson.JSONObject;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.http.HttpMethod;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
@ -10,35 +15,28 @@ import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.servlet.http.HttpServletRequest;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.http.HttpMethod;
import com.alibaba.fastjson.JSONObject;
/**
* http
*
* @author show
* @date 14:23 2019/5/29
*
* @author jeecg
* @date 20210621
*/
public class HttpUtils {
/**
* URLbody
*
* @author show
* @date 14:24 2019/5/29
*
* @author jeecg
* @date 20210621
* @param request
*/
public static SortedMap<String, String> getAllParams(HttpServletRequest request) throws IOException {
SortedMap<String, String> result = new TreeMap<>();
// 获取URL上最后带逗号的参数变量 sys/dict/getDictItems/sys_user,realname,username
String pathVariable = request.getRequestURI().substring(request.getRequestURI().lastIndexOf("/")+1);
if(pathVariable.contains(",")){
result.put(SignUtil.xPathVariable,pathVariable);
String pathVariable = request.getRequestURI().substring(request.getRequestURI().lastIndexOf("/") + 1);
if (pathVariable.contains(",")) {
result.put(SignUtil.xPathVariable, pathVariable);
}
// 获取URL上的参数
Map<String, String> urlParams = getUrlParams(request);
@ -59,11 +57,45 @@ public class HttpUtils {
return result;
}
/**
* URLbody
*
* @author jeecg
* @date 20210621
* @param queryString
*/
public static SortedMap<String, String> getAllParams(String url, String queryString, byte[] body, String method)
throws IOException {
SortedMap<String, String> result = new TreeMap<>();
// 获取URL上最后带逗号的参数变量 sys/dict/getDictItems/sys_user,realname,username
String pathVariable = url.substring(url.lastIndexOf("/") + 1);
if (pathVariable.contains(",")) {
result.put(SignUtil.xPathVariable, pathVariable);
}
// 获取URL上的参数
Map<String, String> urlParams = getUrlParams(queryString);
for (Map.Entry entry : urlParams.entrySet()) {
result.put((String)entry.getKey(), (String)entry.getValue());
}
Map<String, String> allRequestParam = new HashMap<>(16);
// get请求不需要拿body参数
if (!HttpMethod.GET.name().equals(method)) {
allRequestParam = getAllRequestParam(body);
}
// 将URL的参数和body参数进行合并
if (allRequestParam != null) {
for (Map.Entry entry : allRequestParam.entrySet()) {
result.put((String)entry.getKey(), (String)entry.getValue());
}
}
return result;
}
/**
* Body
*
* @author show
* @date 15:04 2019/5/30
*
* @date 15:04 20210621
* @param request
*/
public static Map<String, String> getAllRequestParam(final HttpServletRequest request) throws IOException {
@ -79,15 +111,29 @@ public class HttpUtils {
return JSONObject.parseObject(wholeStr.toString(), Map.class);
}
/**
* Body
*
* @date 15:04 20210621
* @param body
*/
public static Map<String, String> getAllRequestParam(final byte[] body) throws IOException {
if(body==null){
return null;
}
String wholeStr = new String(body);
// 转化成json对象
return JSONObject.parseObject(wholeStr.toString(), Map.class);
}
/**
* URLMap
*
* @author show
*
* @param request
*/
public static Map<String, String> getUrlParams(HttpServletRequest request) {
Map<String, String> result = new HashMap<>(16);
if(oConvertUtils.isEmpty(request.getQueryString())){
if (oConvertUtils.isEmpty(request.getQueryString())) {
return result;
}
String param = "";
@ -103,4 +149,28 @@ public class HttpUtils {
}
return result;
}
/**
* URLMap
*
* @param queryString
*/
public static Map<String, String> getUrlParams(String queryString) {
Map<String, String> result = new HashMap<>(16);
if (oConvertUtils.isEmpty(queryString)) {
return result;
}
String param = "";
try {
param = URLDecoder.decode(queryString, "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
String[] params = param.split("&");
for (String s : params) {
int index = s.indexOf("=");
result.put(s.substring(0, index), s.substring(index + 1));
}
return result;
}
}

@ -1,28 +1,47 @@
package org.jeecg.config;
import feign.Feign;
import feign.Logger;
import feign.RequestInterceptor;
import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.SortedMap;
import javax.servlet.http.HttpServletRequest;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.config.FeignConfig;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.PathMatcherUtil;
import org.jeecg.config.sign.interceptor.SignAuthConfiguration;
import org.jeecg.config.sign.util.HttpUtils;
import org.jeecg.config.sign.util.SignUtil;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.http.MediaType;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.alibaba.fastjson.support.springfox.SwaggerJsonSerializer;
import feign.Feign;
import feign.Logger;
import feign.RequestInterceptor;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;
import lombok.extern.slf4j.Slf4j;
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
@ -39,11 +58,36 @@ public class FeignConfig {
log.info("Feign request: {}", request.getRequestURI());
// 将token信息放入header中
String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
if(token==null){
if(token==null || "".equals(token)){
token = request.getParameter("token");
}
log.info("Feign request token: {}", token);
requestTemplate.header(CommonConstant.X_ACCESS_TOKEN, token);
//根据URL地址过滤请求 【字典表参数签名验证】
if (PathMatcherUtil.matches(Arrays.asList(SignAuthConfiguration.urlList),requestTemplate.path())) {
try {
log.info("============================ [begin] fegin starter url ============================");
log.info(requestTemplate.path());
log.info(requestTemplate.method());
String queryLine = requestTemplate.queryLine();
if(queryLine!=null && queryLine.startsWith("?")){
queryLine = queryLine.substring(1);
}
log.info(queryLine);
if(requestTemplate.body()!=null){
log.info(new String(requestTemplate.body()));
}
SortedMap<String, String> allParams = HttpUtils.getAllParams(requestTemplate.path(),queryLine,requestTemplate.body(),requestTemplate.method());
String sign = SignUtil.getParamsSign(allParams);
log.info(" Feign request params sign: {}",sign);
log.info("============================ [end] fegin starter url ============================");
requestTemplate.header(CommonConstant.X_SIGN, sign);
requestTemplate.header(CommonConstant.X_TIMESTAMP, DateUtils.getCurrentTimestamp().toString());
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
}
@ -74,4 +118,42 @@ public class FeignConfig {
public Encoder multipartFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
return new SpringFormEncoder(new SpringEncoder(messageConverters));
}
// update-begin--Author:sunjianlei Date:20210604 for 给 Feign 添加 FastJson 的解析支持 ----------
@Bean
public Encoder feignEncoder() {
return new SpringEncoder(feignHttpMessageConverter());
}
@Bean
public Decoder feignDecoder() {
return new SpringDecoder(feignHttpMessageConverter());
}
/**
* fastjson
*
* @return
*/
private ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(this.getFastJsonConverter());
return () -> httpMessageConverters;
}
private FastJsonHttpMessageConverter getFastJsonConverter() {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
List<MediaType> supportedMediaTypes = new ArrayList<>();
MediaType mediaTypeJson = MediaType.valueOf(MediaType.APPLICATION_JSON_VALUE);
supportedMediaTypes.add(mediaTypeJson);
converter.setSupportedMediaTypes(supportedMediaTypes);
FastJsonConfig config = new FastJsonConfig();
config.getSerializeConfig().put(JSON.class, new SwaggerJsonSerializer());
config.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);
converter.setFastJsonConfig(config);
return converter;
}
// update-end--Author:sunjianlei Date:20210604 for 给 Feign 添加 FastJson 的解析支持 ----------
}

Loading…
Cancel
Save