From ef4a293f39226a685820ec3418783ec8d6279e1d Mon Sep 17 00:00:00 2001
From: zhangdaiscott <zhangdaiscott@163.com>
Date: Tue, 22 Jun 2021 15:56:04 +0800
Subject: [PATCH] HW21-0499 表字典接口存在SQL注入漏洞,增加签名拦截器 online表单数据源配置,数据库类型识别错误 #2671

---
 ant-design-vue-jeecg/src/api/manage.js                                                                                            |  22 +++++++++++++++++++---
 ant-design-vue-jeecg/src/utils/encryption/signMd5Utils.js                                                                         | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/DateUtils.java                                |   6 +++++-
 jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthConfiguration.java        |  27 +++++++++++++++++++++++++++
 jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthInterceptor.java          |  82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/util/BodyReaderHttpServletRequestWrapper.java | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/util/HttpUtils.java                           | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/util/SignUtil.java                            |  49 +++++++++++++++++++++++++++++++++++++++++++++++++
 jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/config/init/CodeGenerateDbConfig.java                                 |  11 +++++++++++
 jeecg-boot/pom.xml                                                                                                                |   2 +-
 10 files changed, 534 insertions(+), 5 deletions(-)
 create mode 100644 ant-design-vue-jeecg/src/utils/encryption/signMd5Utils.js
 create mode 100644 jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthConfiguration.java
 create mode 100644 jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthInterceptor.java
 create mode 100644 jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/util/BodyReaderHttpServletRequestWrapper.java
 create mode 100644 jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/util/HttpUtils.java
 create mode 100644 jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/util/SignUtil.java

diff --git a/ant-design-vue-jeecg/src/api/manage.js b/ant-design-vue-jeecg/src/api/manage.js
index 48f8618..4abe6dc 100644
--- a/ant-design-vue-jeecg/src/api/manage.js
+++ b/ant-design-vue-jeecg/src/api/manage.js
@@ -1,5 +1,6 @@
 import Vue from 'vue'
 import { axios } from '@/utils/request'
+import signMd5Utils from '@/utils/encryption/signMd5Utils'
 
 const api = {
   user: '/mock/api/user',
@@ -13,19 +14,29 @@ export default api
 
 //post
 export function postAction(url,parameter) {
+  let sign = signMd5Utils.getSign(url, parameter);
+  //将签名和时间戳,添加在请求接口 Header
+  let signHeader = {"X-Sign": sign,"X-TIMESTAMP": signMd5Utils.getDateTimeToString()};
+
   return axios({
     url: url,
     method:'post' ,
-    data: parameter
+    data: parameter,
+    headers: signHeader
   })
 }
 
 //post method= {post | put}
 export function httpAction(url,parameter,method) {
+  let sign = signMd5Utils.getSign(url, parameter);
+  //将签名和时间戳,添加在请求接口 Header
+  let signHeader = {"X-Sign": sign,"X-TIMESTAMP": signMd5Utils.getDateTimeToString()};
+
   return axios({
     url: url,
     method:method ,
-    data: parameter
+    data: parameter,
+    headers: signHeader
   })
 }
 
@@ -40,10 +51,15 @@ export function putAction(url,parameter) {
 
 //get
 export function getAction(url,parameter) {
+  let sign = signMd5Utils.getSign(url, parameter);
+  //将签名和时间戳,添加在请求接口 Header
+  let signHeader = {"X-Sign": sign,"X-TIMESTAMP": signMd5Utils.getDateTimeToString()};
+
   return axios({
     url: url,
     method: 'get',
-    params: parameter
+    params: parameter,
+    headers: signHeader
   })
 }
 
diff --git a/ant-design-vue-jeecg/src/utils/encryption/signMd5Utils.js b/ant-design-vue-jeecg/src/utils/encryption/signMd5Utils.js
new file mode 100644
index 0000000..c748750
--- /dev/null
+++ b/ant-design-vue-jeecg/src/utils/encryption/signMd5Utils.js
@@ -0,0 +1,127 @@
+import md5 from 'md5'
+//签名密钥串(前后端要一致,正式发布请自行修改)
+const signatureSecret = "dd05f1c54d63749eda95f9fa6d49v442a";
+
+export default class signMd5Utils {
+  /**
+   * json参数升序
+   * @param jsonObj 发送参数
+   */
+
+  static sortAsc(jsonObj) {
+    let arr = new Array();
+    let num = 0;
+    for (let i in jsonObj) {
+      arr[num] = i;
+      num++;
+    }
+    let sortArr = arr.sort();
+    let sortObj = {};
+    for (let i in sortArr) {
+      sortObj[sortArr[i]] = jsonObj[sortArr[i]];
+    }
+    return sortObj;
+  }
+
+
+  /**
+   * @param url 请求的url,应该包含请求参数(url的?后面的参数)
+   * @param requestParams 请求参数(POST的JSON参数)
+   * @returns {string} 获取签名
+   */
+  static getSign(url, requestParams) {
+    let urlParams = this.parseQueryString(url);
+    let jsonObj = this.mergeObject(urlParams, requestParams);
+    //console.log("sign jsonObj: ",jsonObj)
+    let requestBody = this.sortAsc(jsonObj);
+    console.log("sign requestBody: ",requestBody)
+    return md5(JSON.stringify(requestBody) + signatureSecret).toUpperCase();
+  }
+
+  /**
+   * @param url 请求的url
+   * @returns {{}} 将url中请求参数组装成json对象(url的?后面的参数)
+   */
+  static parseQueryString(url) {
+    let urlReg = /^[^\?]+\?([\w\W]+)$/,
+      paramReg = /([^&=]+)=([\w\W]*?)(&|$|#)/g,
+      urlArray = urlReg.exec(url),
+      result = {};
+
+    // 获取URL上最后带逗号的参数变量 sys/dict/getDictItems/sys_user,realname,username
+    let lastpathVariable = url.substring(url.lastIndexOf('/') + 1);
+    if(lastpathVariable.includes(",")){
+      if(lastpathVariable.includes("?")){
+        lastpathVariable = lastpathVariable.substring(0, lastpathVariable.indexOf("?"));
+      }
+      result["x-path-variable"] = lastpathVariable;
+    }
+    if (urlArray && urlArray[1]) {
+      let paramString = urlArray[1], paramResult;
+      while ((paramResult = paramReg.exec(paramString)) != null) {
+        //数字值转为string类型,前后端加密规则保持一致
+        if(this.myIsNaN(paramResult[2])){
+          paramResult[2] = paramResult[2].toString()
+        }
+        result[paramResult[1]] = paramResult[2];
+      }
+    }
+    return result;
+  }
+
+  /**
+   * @returns {*} 将两个对象合并成一个
+   */
+  static mergeObject(objectOne, objectTwo) {
+    if (objectTwo && Object.keys(objectTwo).length > 0) {
+      for (let key in objectTwo) {
+        if (objectTwo.hasOwnProperty(key) === true) {
+          //数字值转为string类型,前后端加密规则保持一致
+          if(this.myIsNaN(objectTwo[key])){
+            objectTwo[key] = objectTwo[key].toString()
+          }
+          objectOne[key] = objectTwo[key];
+        }
+      }
+    }
+    return objectOne;
+  }
+
+  static urlEncode(param, key, encode) {
+    if (param == null) return '';
+    let paramStr = '';
+    let t = typeof (param);
+    if (t == 'string' || t == 'number' || t == 'boolean') {
+      paramStr += '&' + key + '=' + ((encode == null || encode) ? encodeURIComponent(param) : param);
+    } else {
+      for (let i in param) {
+        let k = key == null ? i : key + (param instanceof Array ? '[' + i + ']' : '.' + i);
+        paramStr += this.urlEncode(param[i], k, encode);
+      }
+    }
+    return paramStr;
+  };
+
+  static getDateTimeToString() {
+    const date_ = new Date()
+    const year = date_.getFullYear()
+    let month = date_.getMonth() + 1
+    let day = date_.getDate()
+    if (month < 10) month = '0' + month
+    if (day < 10) day = '0' + day
+    let hours = date_.getHours()
+    let mins = date_.getMinutes()
+    let secs = date_.getSeconds()
+    const msecs = date_.getMilliseconds()
+    if (hours < 10) hours = '0' + hours
+    if (mins < 10) mins = '0' + mins
+    if (secs < 10) secs = '0' + secs
+    if (msecs < 10) secs = '0' + msecs
+    return year + '' + month + '' + day + '' + hours + '' + mins + '' + secs
+  }
+    // true:数值型的,false:非数值型
+  static myIsNaN(value) {
+    return typeof value === 'number' && !isNaN(value);
+  }
+
+}
\ No newline at end of file
diff --git a/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/DateUtils.java b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/DateUtils.java
index af5dfe7..6a5f14c 100644
--- a/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/DateUtils.java
+++ b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/DateUtils.java
@@ -291,7 +291,7 @@ public class DateUtils extends PropertyEditorSupport {
         Date dt = new Date();
         DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
         String nowTime = df.format(dt);
-        java.sql.Timestamp buydate = java.sql.Timestamp.valueOf(nowTime);
+        Timestamp buydate = Timestamp.valueOf(nowTime);
         return buydate;
     }
 
@@ -616,6 +616,10 @@ public class DateUtils extends PropertyEditorSupport {
         return 0;
     }
 
+    public static Long getCurrentTimestamp() {
+        return Long.valueOf(DateUtils.yyyymmddhhmmss.get().format(new Date()));
+    }
+
     /**
      * String类型 转换为Date, 如果参数长度为10 转换格式”yyyy-MM-dd“ 如果参数长度为19 转换格式”yyyy-MM-dd
      * HH:mm:ss“ * @param text String类型的时间值
diff --git a/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthConfiguration.java b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthConfiguration.java
new file mode 100644
index 0000000..0414af2
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthConfiguration.java
@@ -0,0 +1,27 @@
+package org.jeecg.config.sign.interceptor;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * online 拦截器配置
+ */
+@Configuration
+public class SignAuthConfiguration implements WebMvcConfigurer {
+
+    @Bean
+    public SignAuthInterceptor signAuthInterceptor() {
+        return new SignAuthInterceptor();
+    }
+
+    @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);
+    }
+}
diff --git a/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthInterceptor.java b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthInterceptor.java
new file mode 100644
index 0000000..4dcfa2c
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthInterceptor.java
@@ -0,0 +1,82 @@
+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 org.jeecg.common.api.vo.Result;
+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;
+
+/**
+ * 签名拦截器
+ * @author qinfeng
+ */
+@Slf4j
+public class SignAuthInterceptor implements HandlerInterceptor {
+    /**
+     * 5分钟有效期
+     */
+    private final static long MAX_EXPIRE = 5 * 60;
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        log.debug("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");
+
+        //1.校验时间有消息
+        try {
+            DateUtils.parseDate(timesTamp, "yyyyMMddHHmmss");
+        } catch (Exception e) {
+            throw new IllegalArgumentException("签名验证失败:X-TIMESTAMP格式必须为:yyyyMMddHHmmss");
+        }
+        Long clientTimestamp = Long.parseLong(timesTamp);
+        //判断时间戳 timestamp=201808091113
+        if ((DateUtils.getCurrentTimestamp() - clientTimestamp) > MAX_EXPIRE) {
+            throw new IllegalArgumentException("签名验证失败:X-TIMESTAMP已过期");
+        }
+
+        //2.校验签名
+        boolean isSigned = SignUtil.verifySign(allParams,headerSign);
+
+        if (isSigned) {
+            log.debug("Sign 签名通过!Header Sign : {}",headerSign);
+            return true;
+        } 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");
+            PrintWriter out = response.getWriter();
+            Result<?> result = Result.error("Sign签名校验失败!");
+            out.print(JSON.toJSON(result));
+            return false;
+        }
+    }
+
+}
diff --git a/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/util/BodyReaderHttpServletRequestWrapper.java b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/util/BodyReaderHttpServletRequestWrapper.java
new file mode 100644
index 0000000..1e59647
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/util/BodyReaderHttpServletRequestWrapper.java
@@ -0,0 +1,107 @@
+package org.jeecg.config.sign.util;
+
+import javax.servlet.ReadListener;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import java.io.*;
+import java.nio.charset.Charset;
+
+/**
+ * 保存过滤器里面的流
+ * 
+ * @author show
+ * @date 10:03 2019/5/30
+ */
+public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
+
+    private final byte[] body;
+
+    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) {
+
+        super(request);
+        String sessionStream = getBodyString(request);
+        body = sessionStream.getBytes(Charset.forName("UTF-8"));
+    }
+
+    /**
+     * 获取请求Body
+     *
+     * @param request
+     * @return
+     */
+    public String getBodyString(final ServletRequest request) {
+
+        StringBuilder sb = new StringBuilder();
+        try (InputStream inputStream = cloneInputStream(request.getInputStream());
+            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")))) {
+            String line;
+            while ((line = reader.readLine()) != null) {
+                sb.append(line);
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Description: 复制输入流</br>
+     *
+     * @param inputStream
+     * @return</br>
+     */
+    public InputStream cloneInputStream(ServletInputStream inputStream) {
+
+        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        byte[] buffer = new byte[1024];
+        int len;
+        try {
+            while ((len = inputStream.read(buffer)) > -1) {
+                byteArrayOutputStream.write(buffer, 0, len);
+            }
+            byteArrayOutputStream.flush();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
+    }
+
+    @Override
+    public BufferedReader getReader() {
+
+        return new BufferedReader(new InputStreamReader(getInputStream()));
+    }
+
+    @Override
+    public ServletInputStream getInputStream() {
+
+        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
+        return new ServletInputStream() {
+
+            @Override
+            public int read() {
+
+                return bais.read();
+            }
+
+            @Override
+            public boolean isFinished() {
+
+                return false;
+            }
+
+            @Override
+            public boolean isReady() {
+
+                return false;
+            }
+
+            @Override
+            public void setReadListener(ReadListener readListener) {
+
+            }
+        };
+    }
+}
\ No newline at end of file
diff --git a/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/util/HttpUtils.java b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/util/HttpUtils.java
new file mode 100644
index 0000000..9e51bae
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/util/HttpUtils.java
@@ -0,0 +1,106 @@
+package org.jeecg.config.sign.util;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.HashMap;
+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
+ */
+public class HttpUtils {
+
+    /**
+     * 将URL的参数和body参数合并
+     * 
+     * @author show
+     * @date 14:24 2019/5/29
+     * @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);
+        }
+        // 获取URL上的参数
+        Map<String, String> urlParams = getUrlParams(request);
+        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(request.getMethod())) {
+            allRequestParam = getAllRequestParam(request);
+        }
+        // 将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
+     * @param request
+     */
+    public static Map<String, String> getAllRequestParam(final HttpServletRequest request) throws IOException {
+
+        BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
+        String str = "";
+        StringBuilder wholeStr = new StringBuilder();
+        // 一行一行的读取body体里面的内容;
+        while ((str = reader.readLine()) != null) {
+            wholeStr.append(str);
+        }
+        // 转化成json对象
+        return JSONObject.parseObject(wholeStr.toString(), Map.class);
+    }
+
+    /**
+     * 将URL请求参数转换成Map
+     * 
+     * @author show
+     * @param request
+     */
+    public static Map<String, String> getUrlParams(HttpServletRequest request) {
+        Map<String, String> result = new HashMap<>(16);
+        if(oConvertUtils.isEmpty(request.getQueryString())){
+            return result;
+        }
+        String param = "";
+        try {
+            param = URLDecoder.decode(request.getQueryString(), "utf-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        String[] params = param.split("&");
+        for (String s : params) {
+            int index = s.indexOf("=");
+            result.put(s.substring(0, index), s.substring(index + 1));
+        }
+        return result;
+    }
+}
\ No newline at end of file
diff --git a/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/util/SignUtil.java b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/util/SignUtil.java
new file mode 100644
index 0000000..658d1c1
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/util/SignUtil.java
@@ -0,0 +1,49 @@
+package org.jeecg.config.sign.util;
+
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.util.DigestUtils;
+import org.springframework.util.StringUtils;
+
+import java.util.SortedMap;
+
+/**
+ * 签名工具类
+ * 
+ * @author show
+ * @date 10:01 2019/5/30
+ */
+@Slf4j
+public class SignUtil {
+    //签名密钥串(前后端要一致,正式发布请自行修改)
+    private static final String signatureSecret = "dd05f1c54d63749eda95f9fa6d49v442a";
+    public static final String xPathVariable = "x-path-variable";
+
+    /**
+     * @param params
+     *            所有的请求参数都会在这里进行排序加密
+     * @return 验证签名结果
+     */
+    public static boolean verifySign(SortedMap<String, String> params,String headerSign) {
+        if (params == null || StringUtils.isEmpty(headerSign)) {
+            return false;
+        }
+        // 把参数加密
+        String paramsSign = getParamsSign(params);
+        log.info("Param Sign : {}", paramsSign);
+        return !StringUtils.isEmpty(paramsSign) && headerSign.equals(paramsSign);
+    }
+
+    /**
+     * @param params
+     *            所有的请求参数都会在这里进行排序加密
+     * @return 得到签名
+     */
+    public static String getParamsSign(SortedMap<String, String> params) {
+        //去掉 Url 里的时间戳
+        params.remove("_t");
+        String paramsJsonStr = JSONObject.toJSONString(params);
+        log.info("Param paramsJsonStr : {}", paramsJsonStr);
+        return DigestUtils.md5DigestAsHex((paramsJsonStr+signatureSecret).getBytes()).toUpperCase();
+    }
+}
\ No newline at end of file
diff --git a/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/config/init/CodeGenerateDbConfig.java b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/config/init/CodeGenerateDbConfig.java
index a203293..99bd57e 100644
--- a/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/config/init/CodeGenerateDbConfig.java
+++ b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/config/init/CodeGenerateDbConfig.java
@@ -1,5 +1,6 @@
 package org.jeecg.config.init;
 
+import com.alibaba.druid.filter.config.ConfigTools;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.jeecgframework.codegenerate.database.CodegenDatasourceConfig;
@@ -25,11 +26,21 @@ public class CodeGenerateDbConfig {
     private String password;
     @Value("${spring.datasource.dynamic.datasource.master.driver-class-name:}")
     private String driverClassName;
+    @Value("${spring.datasource.dynamic.datasource.master.druid.public-key:}")
+    private String publicKey;
 
 
     @Bean
     public CodeGenerateDbConfig initCodeGenerateDbConfig() {
         if(StringUtils.isNotBlank(url)){
+            if(StringUtils.isNotBlank(publicKey)){
+                try {
+                    password = ConfigTools.decrypt(publicKey, password);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    log.error(" 代码生成器数据库连接,数据库密码解密失败!");
+                }
+            }
             CodegenDatasourceConfig.initDbConfig(driverClassName,url, username, password);
             log.info(" 代码生成器数据库连接,使用application.yml的DB配置 ###################");
         }
diff --git a/jeecg-boot/pom.xml b/jeecg-boot/pom.xml
index b296e14..da30acb 100644
--- a/jeecg-boot/pom.xml
+++ b/jeecg-boot/pom.xml
@@ -39,7 +39,7 @@
 		<shiro.version>1.7.1</shiro.version>
 		<java-jwt.version>3.11.0</java-jwt.version>
 		<shiro-redis.version>3.1.0</shiro-redis.version>
-		<codegenerate.version>1.3.2</codegenerate.version>
+		<codegenerate.version>1.3.3</codegenerate.version>
 		<autopoi-web.version>1.3.2</autopoi-web.version>
 		<minio.version>8.0.3</minio.version>
 		<justauth-spring-boot-starter.version>1.3.4</justauth-spring-boot-starter.version>
--
libgit2 0.22.2