Commit 293e7d19 by 郑冰晶

数据库加密组件

parent 7d00ea38
...@@ -17,9 +17,31 @@ public class BizResponse<T> extends Response<T> { ...@@ -17,9 +17,31 @@ public class BizResponse<T> extends Response<T> {
super(code, msg); super(code, msg);
} }
public BizResponse(Integer code, String msg, T data) {
super(code, msg, data);
}
public boolean isSuccess() { public boolean isSuccess() {
return getCode() != null && getCode() == CommonConstant.Success.CODE; return getCode() != null && getCode().equals(CommonConstant.Success.CODE);
}
public static <T> BizResponse<T> success(T data) {
return new BizResponse<>(CommonConstant.Success.CODE, CommonConstant.Success.MSG, data);
}
public static <T> BizResponse<T> success(Integer code, String msg) {
return new BizResponse<>(code, msg);
} }
} public static <T> BizResponse<T> success(Integer code, String msg,T data) {
return new BizResponse<>(code, msg, data);
}
public static <T> BizResponse<T> fail(Integer code, String msg) {
return new BizResponse<>(code, msg);
}
public static <T> BizResponse<T> fail(Integer code, String msg, T data) {
return new BizResponse<>(code, msg, data);
}
}
\ No newline at end of file
...@@ -21,6 +21,12 @@ public class Response<T> implements Serializable { ...@@ -21,6 +21,12 @@ public class Response<T> implements Serializable {
this.msg = msg; this.msg = msg;
} }
public Response(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public Integer getCode() { public Integer getCode() {
return code; return code;
} }
...@@ -53,4 +59,4 @@ public class Response<T> implements Serializable { ...@@ -53,4 +59,4 @@ public class Response<T> implements Serializable {
", data=" + data + ", data=" + data +
'}'; '}';
} }
} }
\ No newline at end of file
...@@ -2,17 +2,15 @@ package com.secoo.mall.common.util.http; ...@@ -2,17 +2,15 @@ package com.secoo.mall.common.util.http;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.secoo.mall.common.util.date.DateUtil;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity; import org.apache.http.*;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException; import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig; import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*; import org.apache.http.client.methods.*;
import org.apache.http.client.utils.URIBuilder; import org.apache.http.client.utils.URIBuilder;
import org.apache.http.concurrent.FutureCallback; import org.apache.http.concurrent.FutureCallback;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy; import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.entity.StringEntity; import org.apache.http.entity.StringEntity;
...@@ -21,8 +19,12 @@ import org.apache.http.impl.client.HttpClients; ...@@ -21,8 +19,12 @@ import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients; import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.message.BasicNameValuePair; import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContexts; import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.Args;
import org.apache.http.util.EntityUtils; import org.apache.http.util.EntityUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -36,7 +38,7 @@ import java.nio.charset.Charset; ...@@ -36,7 +38,7 @@ import java.nio.charset.Charset;
import java.security.KeyManagementException; import java.security.KeyManagementException;
import java.security.KeyStoreException; import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat; import java.time.LocalDateTime;
import java.util.*; import java.util.*;
/** /**
...@@ -48,13 +50,16 @@ public class HttpClientUtils { ...@@ -48,13 +50,16 @@ public class HttpClientUtils {
private static Logger LOG = LoggerFactory.getLogger(HttpClientUtils.class); private static Logger LOG = LoggerFactory.getLogger(HttpClientUtils.class);
private static PoolingHttpClientConnectionManager connMgr; private static final int CONNECT_TIMEOUT = 10000;
private static RequestConfig requestConfig; private static final int SOCKET_TIMEOUT = 60000;
private static final int CONNECT_TIMEOUT = 100000; private static final int REQUEST_TIMEOUT = 10000;
private static final int SOCKET_TIMEOUT = 100000; private static final int KEEPALIVE_TIMEOUT = 50000;
private static final int REQUEST_TIMEOUT = 2000;
private static final String CHARSET = "UTF-8"; private static final String CHARSET = "UTF-8";
private static final ConnectionKeepAliveStrategy connectionKeepAliveStrategy;
private static final PoolingHttpClientConnectionManager connMgr;
private static RequestConfig requestConfig;
static { static {
// 设置连接池 // 设置连接池
connMgr = new PoolingHttpClientConnectionManager(); connMgr = new PoolingHttpClientConnectionManager();
...@@ -69,8 +74,25 @@ public class HttpClientUtils { ...@@ -69,8 +74,25 @@ public class HttpClientUtils {
configBuilder.setConnectionRequestTimeout(REQUEST_TIMEOUT); configBuilder.setConnectionRequestTimeout(REQUEST_TIMEOUT);
// 等待数据超时时间 // 等待数据超时时间
configBuilder.setSocketTimeout(SOCKET_TIMEOUT); configBuilder.setSocketTimeout(SOCKET_TIMEOUT);
LOG.debug("Http参数设置,连接超时时间[{}],Socket超时时间[{}],请求超时时间[{}]", CONNECT_TIMEOUT, SOCKET_TIMEOUT); LOG.debug("Http参数设置,连接超时时间[{}],Socket超时时间[{}],请求超时时间[{}]", CONNECT_TIMEOUT, SOCKET_TIMEOUT,REQUEST_TIMEOUT);
requestConfig = configBuilder.build(); requestConfig = configBuilder.build();
connectionKeepAliveStrategy = new ConnectionKeepAliveStrategy() {
@Override
public long getKeepAliveDuration(HttpResponse httpResponse, HttpContext httpContext) {
Args.notNull(httpResponse, "HTTP response");
final HeaderElementIterator it = new BasicHeaderElementIterator(httpResponse.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
final HeaderElement he = it.nextElement();
final String param = he.getName();
final String value = he.getValue();
if (value != null && param.equalsIgnoreCase("timeout")) {
return Long.parseLong(value) * 1000;
}
}
return KEEPALIVE_TIMEOUT;
}
};
} }
/** /**
...@@ -103,7 +125,7 @@ public class HttpClientUtils { ...@@ -103,7 +125,7 @@ public class HttpClientUtils {
String result = doGet(url, new HashMap<>(10)); String result = doGet(url, new HashMap<>(10));
return result; return result;
} catch (Exception e) { } catch (Exception e) {
LOG.error("发送 GET 请求ERROR :{}", e); LOG.error("发送 GET 请求ERROR:", e);
} }
return ""; return "";
} }
...@@ -125,7 +147,7 @@ public class HttpClientUtils { ...@@ -125,7 +147,7 @@ public class HttpClientUtils {
String result = null; String result = null;
if (StringUtils.isEmpty(url)) { if (StringUtils.isEmpty(url)) {
LOG.debug("warn:doGet url is null or '' "); LOG.warn("doGet url is null or '' ");
return result; return result;
} }
...@@ -133,8 +155,11 @@ public class HttpClientUtils { ...@@ -133,8 +155,11 @@ public class HttpClientUtils {
for (Map.Entry<String, Object> entry : params.entrySet()) { for (Map.Entry<String, Object> entry : params.entrySet()) {
pairList.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString())); pairList.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));
} }
CloseableHttpClient httpclient = HttpClients.custom().setDefaultRequestConfig(requestConfig) CloseableHttpClient httpclient = HttpClients.custom()
.setConnectionManager(connMgr).build(); .setDefaultRequestConfig(requestConfig)
.setConnectionManager(connMgr)
.setKeepAliveStrategy(connectionKeepAliveStrategy)
.build();
CloseableHttpResponse response = null; CloseableHttpResponse response = null;
InputStream instream = null; InputStream instream = null;
try { try {
...@@ -169,15 +194,16 @@ public class HttpClientUtils { ...@@ -169,15 +194,16 @@ public class HttpClientUtils {
} }
public static HttpEntity doGetByDefault(String url) throws Exception { public static HttpEntity doGetByDefault(String url) throws Exception {
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultRequestConfig(requestConfig)
CloseableHttpClient httpclient = HttpClients.custom().setDefaultRequestConfig(requestConfig) .setConnectionManager(connMgr)
.setConnectionManager(connMgr).build(); .setKeepAliveStrategy(connectionKeepAliveStrategy)
.build();
HttpGet httpGet = new HttpGet(url); HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse response = httpclient.execute(httpGet); CloseableHttpResponse response = httpclient.execute(httpGet);
LOG.info("doGet statusCode:{}", response.getStatusLine().getStatusCode()); LOG.debug("doGet statusCode:{}", response.getStatusLine().getStatusCode());
return response.getEntity(); return response.getEntity();
...@@ -186,8 +212,11 @@ public class HttpClientUtils { ...@@ -186,8 +212,11 @@ public class HttpClientUtils {
public static String doDelete(String url, Map<String, String> headers, Map<String, String> params) throws Exception { public static String doDelete(String url, Map<String, String> headers, Map<String, String> params) throws Exception {
CloseableHttpClient httpclient = HttpClients.custom().setDefaultRequestConfig(requestConfig) CloseableHttpClient httpclient = HttpClients.custom()
.setConnectionManager(connMgr).build(); .setDefaultRequestConfig(requestConfig)
.setConnectionManager(connMgr)
.setKeepAliveStrategy(connectionKeepAliveStrategy)
.build();
CloseableHttpResponse response = null; CloseableHttpResponse response = null;
InputStream instream = null; InputStream instream = null;
List<NameValuePair> pairList = new ArrayList<>(params.size()); List<NameValuePair> pairList = new ArrayList<>(params.size());
...@@ -209,7 +238,7 @@ public class HttpClientUtils { ...@@ -209,7 +238,7 @@ public class HttpClientUtils {
response = httpclient.execute(httpDelete); response = httpclient.execute(httpDelete);
LOG.info("httpDelete statusCode:{}", response.getStatusLine().getStatusCode()); LOG.debug("httpDelete statusCode:{}", response.getStatusLine().getStatusCode());
HttpEntity entity = response.getEntity(); HttpEntity entity = response.getEntity();
if (entity != null) { if (entity != null) {
...@@ -251,8 +280,11 @@ public class HttpClientUtils { ...@@ -251,8 +280,11 @@ public class HttpClientUtils {
}); });
CloseableHttpClient httpclient = HttpClients.custom().setDefaultRequestConfig(requestConfig) CloseableHttpClient httpclient = HttpClients.custom()
.setConnectionManager(connMgr).build(); .setDefaultRequestConfig(requestConfig)
.setConnectionManager(connMgr)
.setKeepAliveStrategy(connectionKeepAliveStrategy)
.build();
CloseableHttpResponse response = null; CloseableHttpResponse response = null;
InputStream instream = null; InputStream instream = null;
try { try {
...@@ -272,7 +304,7 @@ public class HttpClientUtils { ...@@ -272,7 +304,7 @@ public class HttpClientUtils {
} }
response = httpclient.execute(httpPut); response = httpclient.execute(httpPut);
LOG.info("doGet statusCode:{}", response.getStatusLine().getStatusCode()); LOG.debug("doGet statusCode:{}", response.getStatusLine().getStatusCode());
HttpEntity entity = response.getEntity(); HttpEntity entity = response.getEntity();
if (entity != null) { if (entity != null) {
...@@ -315,7 +347,7 @@ public class HttpClientUtils { ...@@ -315,7 +347,7 @@ public class HttpClientUtils {
public static String doPost(String url, Map<String, Object> params) throws Exception { public static String doPost(String url, Map<String, Object> params) throws Exception {
if (StringUtils.isEmpty(url)) { if (StringUtils.isEmpty(url)) {
LOG.info("warn:doPost url is null or '' "); LOG.warn("doPost url is null or '' ");
return null; return null;
} }
List<NameValuePair> pairList = new ArrayList<>(params.size()); List<NameValuePair> pairList = new ArrayList<>(params.size());
...@@ -339,7 +371,7 @@ public class HttpClientUtils { ...@@ -339,7 +371,7 @@ public class HttpClientUtils {
public static String doPost(String url, String xml) throws Exception { public static String doPost(String url, String xml) throws Exception {
if (StringUtils.isEmpty(url)) { if (StringUtils.isEmpty(url)) {
LOG.info("warn:doPost url is null or '' "); LOG.warn("doPost url is null or '' ");
return null; return null;
} }
...@@ -359,7 +391,7 @@ public class HttpClientUtils { ...@@ -359,7 +391,7 @@ public class HttpClientUtils {
public static String doPost(String url, Object json) throws Exception { public static String doPost(String url, Object json) throws Exception {
if (StringUtils.isEmpty(url)) { if (StringUtils.isEmpty(url)) {
LOG.error("warn:doPostByJson url is null or '' "); LOG.warn("doPostByJson url is null or '' ");
return null; return null;
} }
HttpPost httpPost = new HttpPost(url); HttpPost httpPost = new HttpPost(url);
...@@ -373,7 +405,7 @@ public class HttpClientUtils { ...@@ -373,7 +405,7 @@ public class HttpClientUtils {
public static String doPost(String url, HttpEntity entity, Map<String, String> headers) throws Exception { public static String doPost(String url, HttpEntity entity, Map<String, String> headers) throws Exception {
if (StringUtils.isEmpty(url)) { if (StringUtils.isEmpty(url)) {
LOG.error("warn:doPostByJson url is null or '' "); LOG.warn("doPostByJson url is null or '' ");
return null; return null;
} }
HttpPost httpPost = new HttpPost(url); HttpPost httpPost = new HttpPost(url);
...@@ -396,7 +428,7 @@ public class HttpClientUtils { ...@@ -396,7 +428,7 @@ public class HttpClientUtils {
public static String doPostSsl(String apiUrl, Map<String, Object> params) throws Exception { public static String doPostSsl(String apiUrl, Map<String, Object> params) throws Exception {
if (StringUtils.isEmpty(apiUrl)) { if (StringUtils.isEmpty(apiUrl)) {
LOG.info("warn:doPostSSL url is null or '' "); LOG.warn("doPostSSL url is null or '' ");
return null; return null;
} }
...@@ -421,7 +453,7 @@ public class HttpClientUtils { ...@@ -421,7 +453,7 @@ public class HttpClientUtils {
public static String doPostSsl(String apiUrl, Object json) throws Exception { public static String doPostSsl(String apiUrl, Object json) throws Exception {
if (StringUtils.isEmpty(apiUrl)) { if (StringUtils.isEmpty(apiUrl)) {
LOG.info("warn:doPostSSL By Json url is null or '' "); LOG.warn("doPostSSL By Json url is null or '' ");
return null; return null;
} }
...@@ -456,10 +488,17 @@ public class HttpClientUtils { ...@@ -456,10 +488,17 @@ public class HttpClientUtils {
SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()) SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy())
.build(); .build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext);
httpClient = HttpClients.custom().setConnectionManager(connMgr).setSSLSocketFactory(sslsf) httpClient = HttpClients.custom()
.setDefaultRequestConfig(requestConfig).build(); .setConnectionManager(connMgr)
.setSSLSocketFactory(sslsf)
.setDefaultRequestConfig(requestConfig)
.setKeepAliveStrategy(connectionKeepAliveStrategy)
.build();
} else { } else {
httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).setConnectionManager(connMgr) httpClient = HttpClients.custom()
.setDefaultRequestConfig(requestConfig)
.setConnectionManager(connMgr)
.setKeepAliveStrategy(connectionKeepAliveStrategy)
.build(); .build();
} }
response = httpClient.execute(httpPost); response = httpClient.execute(httpPost);
...@@ -497,33 +536,36 @@ public class HttpClientUtils { ...@@ -497,33 +536,36 @@ public class HttpClientUtils {
final String[] content = new String[1]; final String[] content = new String[1];
content[0]=""; content[0]="";
// 传入HttpPost request // 传入HttpPost request
CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom().setDefaultRequestConfig(requestConfig).build(); CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom()
LOG.info("httpclient test begin time:{}", DateUtil.getDateTime()); .setDefaultRequestConfig(requestConfig)
.setKeepAliveStrategy(connectionKeepAliveStrategy)
.build();
LOG.debug("httpclient test begin time:{}", LocalDateTime.now());
httpclient.start(); httpclient.start();
httpclient.execute(request, new FutureCallback<HttpResponse>() { httpclient.execute(request, new FutureCallback<HttpResponse>() {
@Override @Override
public void completed(final HttpResponse response) { public void completed(final HttpResponse response) {
LOG.info(request.getRequestLine() + "->" + response.getStatusLine()); LOG.debug(request.getRequestLine() + "->" + response.getStatusLine());
LOG.info("httpclient test received content time:{}",DateUtil.getDateTime()); LOG.debug("httpclient test received content time:{}",LocalDateTime.now());
try { try {
content[0] = EntityUtils.toString(response.getEntity(), "UTF-8"); content[0] = EntityUtils.toString(response.getEntity(), "UTF-8");
} catch (IOException e) { } catch (IOException e) {
LOG.error("matrix HttpClientUtils asynchronousPost error",e); LOG.error("matrix HttpClientUtils asynchronousPost error",e);
} }
} }
@Override @Override
public void failed(final Exception ex) { public void failed(final Exception ex) {
LOG.info("httpclient test received failed time:{}",DateUtil.getDateTime()); LOG.debug("httpclient test received failed time:{}", LocalDateTime.now());
LOG.error(request.getRequestLine() + "->" + ex); LOG.error(request.getRequestLine() + "->", ex);
} }
@Override @Override
public void cancelled() { public void cancelled() {
LOG.info("httpclient test received cancelled time:{}",DateUtil.getDateTime()); LOG.debug("httpclient test received cancelled time:{}",LocalDateTime.now());
LOG.error(request.getRequestLine() + " cancelled"); LOG.warn(request.getRequestLine() + " cancelled");
} }
}); });
LOG.info("httpclient test end time:{}",DateUtil.getDateTime()); LOG.debug("httpclient test end time:{}",LocalDateTime.now());
return content[0]; return content[0];
} }
......
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>matrix-datasource-security</artifactId>
<groupId>com.secoo.mall</groupId>
<version>2.0.17.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>matrix-datasource-security-core</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
</dependencies>
<build>
<finalName>matrix-datasource-security-starter</finalName>
<plugins>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package com.secoo.mall.datasource.security.algorithm;
import com.secoo.mall.datasource.security.constant.SecurityType;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import org.apache.commons.codec.digest.DigestUtils;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
/**
* AES encrypt algorithm.
*/
@Getter
@Setter
public final class AESSecurityAlgorithm implements SecurityAlgorithm {
private String aesKey;
private byte[] secretKey;
public AESSecurityAlgorithm(String aesKey){
if(aesKey == null || aesKey.length() == 0){
throw new SecurityException("aesKey can not be null!");
}
this.aesKey = aesKey;
this.secretKey = createSecretKey();
}
private byte[] createSecretKey() {
return Arrays.copyOf(DigestUtils.sha1(aesKey), 16);
}
@SneakyThrows(GeneralSecurityException.class)
@Override
public String encrypt(final Object plaintext) {
if (null == plaintext) {
return null;
}
byte[] result = getCipher(Cipher.ENCRYPT_MODE).doFinal(String.valueOf(plaintext).getBytes(StandardCharsets.UTF_8));
return DatatypeConverter.printBase64Binary(result);
}
@SneakyThrows(GeneralSecurityException.class)
@Override
public String decrypt(final String ciphertext) {
if (null == ciphertext) {
return null;
}
byte[] result = getCipher(Cipher.DECRYPT_MODE).doFinal(DatatypeConverter.parseBase64Binary(ciphertext));
return new String(result, StandardCharsets.UTF_8);
}
private Cipher getCipher(final int decryptMode) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException {
Cipher result = Cipher.getInstance(getType());
result.init(decryptMode, new SecretKeySpec(secretKey, getType()));
return result;
}
public String getType() {
return SecurityType.AES;
}
}
package com.secoo.mall.datasource.security.algorithm;
/**
* Encrypt|decrypt algorithm for SPI.
*/
public interface SecurityAlgorithm {
/**
* Encode.
*
* @param plainText plainText
* @return cipherText
*/
String encrypt(Object plainText);
/**
* Decode.
*
* @param cipherText cipherText
* @return plainText
*/
String decrypt(String cipherText);
}
package com.secoo.mall.datasource.security.constant;
public class DBType {
public static final String MYSQL = "MYSQL";
public static final String ORACLE = "ORACLE";
}
package com.secoo.mall.datasource.security.constant;
public class SecurityType {
public static final String DES = "DES";
public static final String AES = "AES";
}
package com.secoo.mall.datasource.security.constant;
public class SymbolConstants {
public static final String SEPARATOR = ",";
public static final String EQUAL = "=";
public static final String SEPARATOR_FEN = ";";
}
package com.secoo.mall.datasource.security.exception;
public class SecurityException extends RuntimeException{
public SecurityException(String message) {
super(message);
}
public SecurityException(Throwable e) {
super(e);
}
public SecurityException(String message, Throwable e) {
super(message, e);
}
}
package com.secoo.mall.datasource.security.exception;
public class TableColumnException extends RuntimeException {
private String message;
public TableColumnException(String message) {
this.message = message;
}
public TableColumnException(String message, String message1) {
super(message);
this.message = message1;
}
public TableColumnException(String message, Throwable cause, String message1) {
super(message, cause);
this.message = message1;
}
public TableColumnException(Throwable cause, String message) {
super(cause);
this.message = message;
}
public TableColumnException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, String message1) {
super(message, cause, enableSuppression, writableStackTrace);
this.message = message1;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
\ No newline at end of file
package com.secoo.mall.datasource.security.filter;
import com.alibaba.druid.filter.FilterEventAdapter;
import com.alibaba.druid.proxy.jdbc.ResultSetProxy;
import com.alibaba.druid.proxy.jdbc.StatementProxy;
import com.secoo.mall.datasource.security.rule.ColumnRule;
import com.secoo.mall.datasource.security.rule.TableRule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public abstract class AbsSecurityFilter extends FilterEventAdapter {
private static final Logger log = LoggerFactory.getLogger(AbsSecurityFilter.class);
public static Charset charset = StandardCharsets.UTF_8;
private String dbType;
/**
* 加密是否开启
*/
private boolean enabled = true;
/**
* 是否启用并行处理
*/
private boolean parallelEnabled;
/**
* 建议core=max
*/
private int corePoolSize;
/**
* 建议core=max
*/
private int maxPoolSize;
private Map<String, Map<String, ColumnRule>> tableRuleMap;
private ExecutorService parallelExecutor;
public String getDbType() {
return dbType;
}
public void setDbType(String dbType) {
this.dbType = dbType;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isParallelEnabled() {
return parallelEnabled;
}
public void setParallelEnabled(boolean parallelEnabled) {
this.parallelEnabled = parallelEnabled;
}
public int getCorePoolSize() {
return corePoolSize;
}
public void setCorePoolSize(int corePoolSize) {
this.corePoolSize = corePoolSize;
}
public int getMaxPoolSize() {
return maxPoolSize;
}
public void setMaxPoolSize(int maxPoolSize) {
this.maxPoolSize = maxPoolSize;
}
public Map<String, Map<String, ColumnRule>> getTableRuleMap() {
return tableRuleMap;
}
public ExecutorService getParallelExecutor() {
return parallelExecutor;
}
protected void resultSetOpenAfter(ResultSetProxy resultSet) {
if (!enabled) {
return;
}
decryptResultSet(resultSet);
}
protected abstract void decryptResultSet(ResultSetProxy resultSet);
protected void statementExecuteBefore(StatementProxy statement, String sql) {
if (!enabled) {
return;
}
this.encryptStatement(statement, sql);
}
protected abstract void encryptStatement(StatementProxy statement,String sql);
private Map<String, Map<String, ColumnRule>> parseTableRules(Set<TableRule> tableRules) {
if (tableRules == null || tableRules.size() == 0) {
return null;
}
Map<String, Map<String, ColumnRule>> tableRuleMap = new HashMap<>();
for(TableRule tableRule: tableRules){
if(tableRule == null || tableRule.getColumnRules() == null || tableRule.getColumnRules().isEmpty()){
continue;
}
Map<String, ColumnRule> columnRuleMap = new HashMap<>();
for(ColumnRule columnRule:tableRule.getColumnRules()){
columnRuleMap.put(columnRule.getLogicColumn(),columnRule);
}
tableRuleMap.put(tableRule.getTableName(),columnRuleMap);
}
return tableRuleMap;
}
public void init(Set<TableRule> tableRules) {
if (enabled) {
return;
}
if (tableRules == null || tableRules.isEmpty()) {
throw new SecurityException("security tableRules is null");
}
this.tableRuleMap = this.parseTableRules(tableRules);
log.info("security tableRules is enable:{}", tableRuleMap);
if (parallelEnabled) {
if (corePoolSize <= 0 || maxPoolSize <= 0) {
throw new SecurityException("corePoolSize(" + corePoolSize + ") or maxPoolSize(" + maxPoolSize + ") is invalid");
}
log.debug("init security parallelExecutors : corePoolSize={}, maxPoolSize={}", this.corePoolSize, this.maxPoolSize);
this.parallelExecutor = new ThreadPoolExecutor(
this.corePoolSize,
this.maxPoolSize,
300,
TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
new NamedThreadFactory("matrix-security-"),
new ThreadPoolExecutor.CallerRunsPolicy()
);
}
}
public void destroy() {
if(this.parallelExecutor != null){
try {
parallelExecutor.shutdown();
parallelExecutor.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
log.warn("interrupted when shutdown the executor:", e);
}
}
}
static class NamedThreadFactory implements ThreadFactory {
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
NamedThreadFactory(String namePrefix) {
this.namePrefix = namePrefix;
}
public Thread newThread(Runnable runnable) {
return new Thread(runnable, namePrefix + threadNumber.getAndIncrement());
}
}
}
package com.secoo.mall.datasource.security.filter;
import com.alibaba.druid.proxy.jdbc.ResultSetProxy;
import com.alibaba.druid.proxy.jdbc.StatementProxy;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.expr.*;
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDeleteStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement;
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor;
import com.alibaba.druid.stat.TableStat;
import com.alibaba.druid.util.JdbcConstants;
import com.mysql.cj.BindValue;
import com.mysql.cj.jdbc.ClientPreparedStatement;
import com.mysql.cj.jdbc.result.ResultSetImpl;
import com.mysql.cj.protocol.ResultsetRows;
import com.mysql.cj.result.Field;
import com.mysql.cj.result.Row;
import com.mysql.cj.util.StringUtils;
import com.secoo.mall.datasource.security.rule.ColumnRule;
import com.secoo.mall.datasource.security.rule.TableRule;
import lombok.extern.slf4j.Slf4j;
import java.sql.Statement;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
@Slf4j
public class MysqlSecurityFilter extends AbsSecurityFilter {
@Override
protected void decryptResultSet(ResultSetProxy resultSet) {
// 结果集
ResultsetRows rows = ((ResultSetImpl) resultSet.getRawObject()).getRows();
// 结果集字段描述
Field[] fields = rows.getMetadata().getFields();
List<Future<Boolean>> futureList = new LinkedList<>();
for (final Field field:fields) {
Map<String, ColumnRule> columnRuleMap = this.getTableRuleMap().get(field.getOriginalTableName());
if (columnRuleMap == null || columnRuleMap.isEmpty()) {
continue;
}
ColumnRule columnRule = columnRuleMap.get(field.getOriginalName());
if (columnRule != null) {
for (int rowIndex = 0; rowIndex < rows.size(); rowIndex++) {
final Row row = rows.get(rowIndex);
decrypt(futureList,columnRule, field, row);
}
}
}
for (Future<Boolean> future : futureList) {
try {
future.get();
} catch (Exception e) {
log.error("解密出现异常,异常部分未解密", e);
}
}
}
@Override
protected void encryptStatement(StatementProxy statement, String sql) {
// 解析sql
List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, this.getDbType());
Statement rawObject = statement.getRawObject();
if (!(rawObject instanceof ClientPreparedStatement)) {
log.debug("不需要处理的statement:{}", rawObject);
return;
}
BindValue[] bindValues = ((ClientPreparedStatement) rawObject).getQueryBindings().getBindValues();
// 解析出语句,通常只有一条,不支持超过一条语句的SQL
for (SQLStatement stmt : stmtList) {
MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor();
stmt.accept(visitor);
List<Future<Boolean>> futureList = new LinkedList<>();
int index = 0;
// 查询语句或删除语句,只有查询条件需要加密
if (stmt instanceof SQLSelectStatement || stmt instanceof MySqlDeleteStatement) {
// 遍历查询条件
for (TableStat.Condition condition : visitor.getConditions()) {
// 遍历查询条件值,一般只有一个,但in/between语句等可能有多个
for (Object conditionValue : condition.getValues()) {
// 解析出条件值为空才是查询条件
if (conditionValue != null) {
continue;
}
TableStat.Column column = condition.getColumn();
Map<String, ColumnRule> columnRuleMap = this.getTableRuleMap().get(column.getTable());
if (columnRuleMap == null || columnRuleMap.isEmpty()) {
continue;
}
// 需要加密的字段
ColumnRule columnRule = columnRuleMap.get(column.getName());
if (columnRule != null) {
encrypt(futureList, columnRule, bindValues[index]);
}
index++;
}
}
}
// 插入语句
else if (stmt instanceof MySqlInsertStatement) {
MySqlInsertStatement insertStmt = (MySqlInsertStatement) stmt;
// 插入语句应该只有一个表
String tableName = insertStmt.getTableName().getSimpleName();
Map<String, ColumnRule> columnRuleMap = this.getTableRuleMap().get(tableName);
if (columnRuleMap == null || columnRuleMap.isEmpty()) {
continue;
}
// valuesSize>1为batch insert语句
int valuesSize = insertStmt.getValuesList().size();
Collection<TableStat.Column> columns = visitor.getColumns();
// 字段数量
int columnSize = columns.size();
for (TableStat.Column column : columns) {
// 需要加密的字段
ColumnRule columnRule = columnRuleMap.get(column.getName());
if (columnRule != null) {
for (int valueIndex = 0; valueIndex < valuesSize; valueIndex++) {
BindValue bindValue = bindValues[index + valueIndex * columnSize];
encrypt(futureList, columnRule, bindValue);
}
}
index++;
}
} else if (stmt instanceof MySqlUpdateStatement) {
MySqlUpdateStatement updateStat = (MySqlUpdateStatement) stmt;
// 更新语句应该只有一个表
String tableName = updateStat.getTableName().getSimpleName();
Map<String, ColumnRule> columnRuleMap = this.getTableRuleMap().get(tableName);
if (columnRuleMap == null || columnRuleMap.isEmpty()) {
continue;
}
// 先处理set语句
for (SQLUpdateSetItem item : updateStat.getItems()) {
SQLExpr column = item.getColumn();
if (item.getValue() instanceof SQLVariantRefExpr && column instanceof SQLIdentifierExpr) {
// 需要加密的字段
String columnName = ((SQLIdentifierExpr) column).getName();
ColumnRule columnRule = columnRuleMap.get(columnName);
if (columnRule != null) {
encrypt(futureList, columnRule, bindValues[index]);
}
index++;
}
}
// 再处理where语句
for (TableStat.Condition condition : visitor.getConditions()) {
// 遍历查询条件值,一般只有一个,但in/between语句等可能有多个
for (Object conditionValue : condition.getValues()) {
// 解析出条件值为空才是查询条件
if (conditionValue == null) {
continue;
}
TableStat.Column column = condition.getColumn();
ColumnRule columnRule = columnRuleMap.get(column.getName());
if (columnRule != null) {
encrypt(futureList, columnRule, bindValues[index]);
}
index++;
}
}
}
// 其他,一般没有了
else {
for (TableStat.Column column : visitor.getColumns()) {
Map<String, ColumnRule> columnRuleMap = this.getTableRuleMap().get(column.getTable());
if (columnRuleMap == null || columnRuleMap.isEmpty()) {
continue;
}
// 需要加密的字段
ColumnRule columnRule = columnRuleMap.get(column.getName());
if (columnRule != null) {
encrypt(futureList, columnRule, bindValues[index]);
}
index++;
}
}
for (Future<Boolean> future : futureList) {
try {
future.get();
} catch (Exception e) {
log.error("加密出现异常,异常部分未加密", e);
}
}
}
}
private void encrypt(List<Future<Boolean>> futureList,final ColumnRule columnRule, final BindValue bindValue) {
final String origValue = getBindValue(bindValue);
if (origValue == null) {
return;
}
if (this.isParallelEnabled()) {
Future<Boolean> future = this.getParallelExecutor().submit(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
encrypt(columnRule, origValue, bindValue);
return true;
}
});
futureList.add(future);
} else {
encrypt(columnRule, origValue, bindValue);
}
}
private void encrypt(ColumnRule columnRule, String origValue, BindValue bindValue) {
String encryptValue = columnRule.getSecurityAlgorithm().encrypt(origValue);
encryptValue = "'" + encryptValue + "'";
bindValue.setByteValue(encryptValue.getBytes(charset));
log.debug("字段加密:columnRule={},origValue={},encryptValue={}", columnRule, origValue, encryptValue);
}
private void decrypt(List<Future<Boolean>> futureList,final ColumnRule columnRule,final Field field, final Row row) {
int index = field.getCollationIndex();
byte[] bytes = row.getBytes(index);
if (bytes != null && bytes.length > 0) {
final String origValue = StringUtils.toString(bytes, charset.name());
if (this.isParallelEnabled()) {
Future<Boolean> future = this.getParallelExecutor().submit(new Callable<Boolean>() {
@Override
public Boolean call() {
decrypt(columnRule, row, origValue, index);
return true;
}
});
futureList.add(future);
} else {
decrypt(columnRule, row, origValue, index);
}
}
}
private void decrypt(ColumnRule columnRule, Row row, String origValue, int index) {
String decryptValue = columnRule.getSecurityAlgorithm().decrypt(origValue);
row.setBytes(index, decryptValue.getBytes(charset));
log.debug("字段解密:columnRule={},origValue={},decryptValue={}", columnRule, origValue, decryptValue);
}
private String getBindValue(BindValue bindValue) {
if (bindValue.isNull()) {
return null;
}
byte[] byteValue = bindValue.getByteValue();
if (byteValue == null || byteValue.length == 0) {
return null;
}
String origValue = StringUtils.toString(byteValue, charset.name());
if ("''".equals(origValue) || "".equals(origValue)) {
return null;
}
// 参数可能自带''单引号,需要去掉''单引号
if (origValue.startsWith("'") && origValue.endsWith("'")) {
origValue = origValue.substring(1, origValue.length() - 1);
}
return origValue;
}
public void init(Set<TableRule> tableRules) {
super.init(tableRules);
this.setDbType(JdbcConstants.MYSQL);
}
}
package com.secoo.mall.datasource.security.rule;
import com.secoo.mall.datasource.security.algorithm.AESSecurityAlgorithm;
import com.secoo.mall.datasource.security.algorithm.SecurityAlgorithm;
public class ColumnRule {
private String encryptType;
/**
* 加密器配置
*/
private String encryptKey;
/**
* 逻辑字段名称
*/
private String logicColumn;
/**
* 明文字段名称
*/
private String plainColumn;
/**
* 加密字段名称
*/
private String cipherColumn;
/**
* 加密器
*/
private SecurityAlgorithm securityAlgorithm;
public String getEncryptKey() {
return encryptKey;
}
public void setEncryptKey(String encryptKey) {
this.encryptKey = encryptKey;
this.securityAlgorithm = new AESSecurityAlgorithm(encryptKey);
}
public String getLogicColumn() {
return logicColumn;
}
public void setLogicColumn(String logicColumn) {
this.logicColumn = logicColumn;
}
public String getPlainColumn() {
return plainColumn;
}
public void setPlainColumn(String plainColumn) {
this.plainColumn = plainColumn;
}
public String getCipherColumn() {
return cipherColumn;
}
public void setCipherColumn(String cipherColumn) {
this.cipherColumn = cipherColumn;
}
public SecurityAlgorithm getSecurityAlgorithm() {
return securityAlgorithm;
}
public void setSecurityAlgorithm(SecurityAlgorithm securityAlgorithm) {
this.securityAlgorithm = securityAlgorithm;
}
}
package com.secoo.mall.datasource.security.rule;
import java.util.Set;
public class DataSourceRule {
/**
* 数据库名
*/
private String datasourceName;
/**
* 表规则
*/
private Set<TableRule> tableRules;
public String getDatasourceName() {
return datasourceName;
}
public void setDatasourceName(String datasourceName) {
this.datasourceName = datasourceName;
}
public Set<TableRule> getTableRules() {
return tableRules;
}
public void setTableRules(Set<TableRule> tableRules) {
this.tableRules = tableRules;
}
}
package com.secoo.mall.datasource.security.rule;
import java.util.Set;
public class TableRule {
/**
* 表名
*/
private String tableName;
/**
* 列规则
*/
private Set<ColumnRule> columnRules;
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public Set<ColumnRule> getColumnRules() {
return columnRules;
}
public void setColumnRules(Set<ColumnRule> columnRules) {
this.columnRules = columnRules;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>matrix-datasource-security</artifactId>
<groupId>com.secoo.mall</groupId>
<version>2.0.17.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>matrix-datasource-security-starter</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.secoo.mall</groupId>
<artifactId>matrix-datasource-security-core</artifactId>
</dependency>
<dependency>
<groupId>com.secoo.mall</groupId>
<artifactId>matrix-datasource-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<finalName>matrix-datasource-security-starter</finalName>
<plugins>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package com.secoo.mall.datasource.security.config;
import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.pool.DruidDataSource;
import com.secoo.mall.datasource.bean.MatrixDataSource;
import com.secoo.mall.datasource.security.constant.DBType;
import com.secoo.mall.datasource.security.filter.MysqlSecurityFilter;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.Map;
@Configuration
@EnableConfigurationProperties(DataSourceSecurityProperties.class)
public class DataSourceSecurityAutoConfiguration implements ApplicationContextAware, SmartInitializingSingleton {
private ConfigurableApplicationContext applicationContext;
private DataSourceSecurityProperties properties;
DataSourceSecurityAutoConfiguration(DataSourceSecurityProperties properties){
this.properties = properties;
}
@Override
public void afterSingletonsInstantiated() {
if(properties == null || properties.getDatasourceRules() == null || properties.getDatasourceRules().size() == 0){
throw new RuntimeException("DataSourceSecurityProperties is null!");
}
Map<String, DataSource> dataSourceBeans = this.applicationContext.getBeansOfType(DataSource.class);
properties.getDatasourceRules().forEach(datasourceRule -> {
DataSource dataSource = dataSourceBeans.get(datasourceRule.getDatasourceName());
if(dataSource == null){
return;
}
DruidDataSource druidDataSource = null;
if(dataSource instanceof DruidDataSource){
druidDataSource = (DruidDataSource) dataSource;
}else if(dataSource instanceof MatrixDataSource){
druidDataSource = (DruidDataSource) ((MatrixDataSource) dataSource).getTargetDataSource(((MatrixDataSource) dataSource).getDsName());
}
if(druidDataSource == null){
return;
}
if(druidDataSource.getDbType().equals(DBType.MYSQL)){
MysqlSecurityFilter securityFilter = new MysqlSecurityFilter();
securityFilter.setEnabled(properties.getEnabled() != null && properties.getEnabled());
securityFilter.setParallelEnabled(properties.getParallelEnabled() != null && properties.getParallelEnabled());
if(securityFilter.isParallelEnabled()){
securityFilter.setCorePoolSize(properties.getCorePoolSize() == null?0:properties.getCorePoolSize());
securityFilter.setMaxPoolSize(properties.getMaxPoolSize() == null?Integer.MAX_VALUE:properties.getMaxPoolSize());
}
securityFilter.init(datasourceRule.getTableRules());
druidDataSource.setProxyFilters(new ArrayList<Filter>(){{add(securityFilter);}});
}
});
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = (ConfigurableApplicationContext) applicationContext;
}
}
package com.secoo.mall.datasource.security.config;
import com.secoo.mall.datasource.security.rule.DataSourceRule;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.Set;
@ConfigurationProperties(prefix = DataSourceSecurityProperties.PREFIX)
public class DataSourceSecurityProperties {
public static final String PREFIX = "spring.matrix.security";
private Boolean enabled = true;
/**
* 是否启用并行处理,默认不启用
*/
private Boolean parallelEnabled = false;
/**
* 建议core=max
*/
private Integer corePoolSize;
/**
* 建议core=max
*/
private Integer maxPoolSize;
/**
* 加解密规则
*/
public Set<DataSourceRule> datasourceRules;
public Boolean getEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public Boolean getParallelEnabled() {
return parallelEnabled;
}
public void setParallelEnabled(Boolean parallelEnabled) {
this.parallelEnabled = parallelEnabled;
}
public Integer getCorePoolSize() {
return corePoolSize;
}
public void setCorePoolSize(Integer corePoolSize) {
this.corePoolSize = corePoolSize;
}
public Integer getMaxPoolSize() {
return maxPoolSize;
}
public void setMaxPoolSize(Integer maxPoolSize) {
this.maxPoolSize = maxPoolSize;
}
public Set<DataSourceRule> getDatasourceRules() {
return datasourceRules;
}
public void setDatasourceRules(Set<DataSourceRule> datasourceRules) {
this.datasourceRules = datasourceRules;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>matrix-datasource</artifactId>
<groupId>com.secoo.mall</groupId>
<version>2.0.17.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>matrix-datasource-security</artifactId>
<packaging>pom</packaging>
<modules>
<module>matrix-datasource-security-core</module>
<module>matrix-datasource-security-starter</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.secoo.mall</groupId>
<artifactId>matrix-datasource-security-core</artifactId>
<version>2.0.17.RELEASE</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
<modules> <modules>
<module>matrix-datasource-core</module> <module>matrix-datasource-core</module>
<module>matrix-datasource-druid</module> <module>matrix-datasource-druid</module>
<module>matrix-datasource-security</module>
</modules> </modules>
<dependencyManagement> <dependencyManagement>
......
...@@ -210,11 +210,6 @@ ...@@ -210,11 +210,6 @@
<artifactId>joda-time</artifactId> <artifactId>joda-time</artifactId>
<version>2.10</version> <version>2.10</version>
</dependency> </dependency>
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>4.0.2</version>
</dependency>
<!--protobuf--> <!--protobuf-->
<dependency> <dependency>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment