2.4.6提交
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.jero.boot</groupId>
|
||||
<artifactId>jero-boot-base</artifactId>
|
||||
<version>2.4.3</version>
|
||||
<version>2.4.6</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<repository>
|
||||
<id>aliyun</id>
|
||||
<name>aliyun Repository</name>
|
||||
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
|
||||
<url>https://maven.aliyun.com/repository/public</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
@@ -22,7 +22,7 @@
|
||||
<repository>
|
||||
<id>jeecg</id>
|
||||
<name>jeecg Repository</name>
|
||||
<url>http://maven.jeecg.org/nexus/content/repositories/jeecg</url>
|
||||
<url>https://maven.jeecg.org/nexus/content/repositories/jeecg</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
@@ -108,6 +108,7 @@
|
||||
<version>${dynamic-datasource-spring-boot-starter.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 数据库驱动 -->
|
||||
<!--mysql-->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
|
||||
+25
@@ -3,6 +3,7 @@ package com.jero.common.api;
|
||||
import com.jero.common.system.vo.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public interface CommonAPI {
|
||||
@@ -85,6 +86,13 @@ public interface CommonAPI {
|
||||
*/
|
||||
public List<DictModel> queryDictItemsByCode(String code);
|
||||
|
||||
/**
|
||||
* 获取有效的数据字典项
|
||||
* @param code
|
||||
* @return
|
||||
*/
|
||||
public List<DictModel> queryEnableDictItemsByCode(String code);
|
||||
|
||||
/**
|
||||
* 13获取表数据字典
|
||||
* @param table
|
||||
@@ -94,4 +102,21 @@ public interface CommonAPI {
|
||||
*/
|
||||
List<DictModel> queryTableDictItemsByCode(String table, String text, String code);
|
||||
|
||||
/**
|
||||
* 14 普通字典的翻译,根据多个dictCode和多条数据,多个以逗号分割
|
||||
* @param dictCodes 例如:user_status,sex
|
||||
* @param keys 例如:1,2,0
|
||||
* @return
|
||||
*/
|
||||
Map<String, List<DictModel>> translateManyDict(String dictCodes, String keys);
|
||||
|
||||
/**
|
||||
* 15 字典表的 翻译,可批量
|
||||
* @param table
|
||||
* @param text
|
||||
* @param code
|
||||
* @param keys 多个用逗号分割
|
||||
* @return
|
||||
*/
|
||||
List<DictModel> translateDictFromTableByKeys(String table, String text, String code, String keys);
|
||||
}
|
||||
|
||||
+216
-15
@@ -1,5 +1,6 @@
|
||||
package com.jero.common.aspect;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
@@ -14,7 +15,7 @@ import com.jero.common.api.CommonAPI;
|
||||
import com.jero.common.api.vo.Result;
|
||||
import com.jero.common.aspect.annotation.Dict;
|
||||
import com.jero.common.constant.CommonConstant;
|
||||
import com.jero.modules.base.service.BaseCommonService;
|
||||
import com.jero.common.system.vo.DictModel;
|
||||
import com.jero.common.util.oConvertUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
@@ -22,9 +23,11 @@ import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import java.lang.reflect.Field;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @Description: 字典aop类
|
||||
@@ -49,14 +52,14 @@ public class DictAspect {
|
||||
|
||||
@Around("excudeService()")
|
||||
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
|
||||
long time1=System.currentTimeMillis();
|
||||
long time1=System.currentTimeMillis();
|
||||
Object result = pjp.proceed();
|
||||
long time2=System.currentTimeMillis();
|
||||
log.debug("获取JSON数据 耗时:"+(time2-time1)+"ms");
|
||||
long start=System.currentTimeMillis();
|
||||
this.parseDictText(result);
|
||||
long end=System.currentTimeMillis();
|
||||
log.debug("解析注入JSON数据 耗时"+(end-start)+"ms");
|
||||
log.debug("注入字典到JSON数据 耗时"+(end-start)+"ms");
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -86,32 +89,46 @@ public class DictAspect {
|
||||
if (result instanceof Result) {
|
||||
if (((Result) result).getResult() instanceof IPage) {
|
||||
List<JSONObject> items = new ArrayList<>();
|
||||
|
||||
//step.1 筛选出加了 Dict 注解的字段列表
|
||||
List<Field> dictFieldList = new ArrayList<>();
|
||||
// 字典数据列表, key = 字典code,value=数据列表
|
||||
Map<String, List<String>> dataListMap = new HashMap<>();
|
||||
|
||||
for (Object record : ((IPage) ((Result) result).getResult()).getRecords()) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
String json="{}";
|
||||
try {
|
||||
//解决@JsonFormat注解解析不了的问题详见SysAnnouncement类的@JsonFormat
|
||||
json = mapper.writeValueAsString(record);
|
||||
json = mapper.writeValueAsString(record);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("json解析失败"+e.getMessage(),e);
|
||||
}
|
||||
JSONObject item = JSONObject.parseObject(json);
|
||||
//update-begin--Author:scott -- Date:20190603 ----for:解决继承实体字段无法翻译问题------
|
||||
//for (Field field : record.getClass().getDeclaredFields()) {
|
||||
// 遍历所有字段,把字典Code取出来,放到 map 里
|
||||
for (Field field : oConvertUtils.getAllFields(record)) {
|
||||
//update-end--Author:scott -- Date:20190603 ----for:解决继承实体字段无法翻译问题------
|
||||
String value = item.getString(field.getName());
|
||||
if (oConvertUtils.isEmpty(value)) {
|
||||
continue;
|
||||
}
|
||||
//update-end--Author:scott -- Date:20190603 ----for:解决继承实体字段无法翻译问题------
|
||||
if (field.getAnnotation(Dict.class) != null) {
|
||||
if (!dictFieldList.contains(field)) {
|
||||
dictFieldList.add(field);
|
||||
}
|
||||
String code = field.getAnnotation(Dict.class).dicCode();
|
||||
String text = field.getAnnotation(Dict.class).dicText();
|
||||
String table = field.getAnnotation(Dict.class).dictTable();
|
||||
String key = String.valueOf(item.get(field.getName()));
|
||||
|
||||
//翻译字典值对应的txt
|
||||
String textValue = translateDictValue(code, text, table, key);
|
||||
|
||||
log.debug(" 字典Val : "+ textValue);
|
||||
log.debug(" __翻译字典字段__ "+field.getName() + CommonConstant.DICT_TEXT_SUFFIX+": "+ textValue);
|
||||
item.put(field.getName() + CommonConstant.DICT_TEXT_SUFFIX, textValue);
|
||||
List<String> dataList;
|
||||
String dictCode = code;
|
||||
if (!StringUtils.isEmpty(table)) {
|
||||
dictCode = String.format("%s,%s,%s", table, text, code);
|
||||
}
|
||||
dataList = dataListMap.computeIfAbsent(dictCode, k -> new ArrayList<>());
|
||||
this.listAddAllDeduplicate(dataList, Arrays.asList(value.split(",")));
|
||||
}
|
||||
//date类型默认转换string格式化日期
|
||||
if (field.getType().getName().equals("java.util.Date")&&field.getAnnotation(JsonFormat.class)==null&&item.get(field.getName())!=null){
|
||||
@@ -121,12 +138,194 @@ public class DictAspect {
|
||||
}
|
||||
items.add(item);
|
||||
}
|
||||
|
||||
//step.2 调用翻译方法,一次性翻译
|
||||
Map<String, List<DictModel>> translText = this.translateAllDict(dataListMap);
|
||||
|
||||
//step.3 将翻译结果填充到返回结果里
|
||||
for (JSONObject record : items) {
|
||||
for (Field field : dictFieldList) {
|
||||
String code = field.getAnnotation(Dict.class).dicCode();
|
||||
String text = field.getAnnotation(Dict.class).dicText();
|
||||
String table = field.getAnnotation(Dict.class).dictTable();
|
||||
|
||||
String fieldDictCode = code;
|
||||
if (!StringUtils.isEmpty(table)) {
|
||||
fieldDictCode = String.format("%s,%s,%s", table, text, code);
|
||||
}
|
||||
|
||||
String value = record.getString(field.getName());
|
||||
if (oConvertUtils.isNotEmpty(value)) {
|
||||
List<DictModel> dictModels = translText.get(fieldDictCode);
|
||||
if(dictModels==null || dictModels.size()==0){
|
||||
continue;
|
||||
}
|
||||
|
||||
String textValue = this.translDictText(dictModels, value);
|
||||
log.debug(" 字典Val : " + textValue);
|
||||
log.debug(" __翻译字典字段__ " + field.getName() + CommonConstant.DICT_TEXT_SUFFIX + ": " + textValue);
|
||||
|
||||
// TODO-sun 测试输出,待删
|
||||
log.debug(" ---- dictCode: " + fieldDictCode);
|
||||
log.debug(" ---- value: " + value);
|
||||
log.debug(" ----- text: " + textValue);
|
||||
log.debug(" ---- dictModels: " + JSON.toJSONString(dictModels));
|
||||
|
||||
record.put(field.getName() + CommonConstant.DICT_TEXT_SUFFIX, textValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
((IPage) ((Result) result).getResult()).setRecords(items);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* list 去重添加
|
||||
*/
|
||||
private void listAddAllDeduplicate(List<String> dataList, List<String> addList) {
|
||||
// 筛选出dataList中没有的数据
|
||||
List<String> filterList = addList.stream().filter(i -> !dataList.contains(i)).collect(Collectors.toList());
|
||||
dataList.addAll(filterList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 一次性把所有的字典都翻译了
|
||||
* 1. 所有的普通数据字典的所有数据只执行一次SQL
|
||||
* 2. 表字典相同的所有数据只执行一次SQL
|
||||
* @param dataListMap
|
||||
* @return
|
||||
*/
|
||||
private Map<String, List<DictModel>> translateAllDict(Map<String, List<String>> dataListMap) {
|
||||
// 翻译后的字典文本,key=dictCode
|
||||
Map<String, List<DictModel>> translText = new HashMap<>();
|
||||
// 需要翻译的数据(有些可以从redis缓存中获取,就不走数据库查询)
|
||||
List<String> needTranslData = new ArrayList<>();
|
||||
//step.1 先通过redis中获取缓存字典数据
|
||||
for (String dictCode : dataListMap.keySet()) {
|
||||
List<String> dataList = dataListMap.get(dictCode);
|
||||
if (dataList.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
// 表字典需要翻译的数据
|
||||
List<String> needTranslDataTable = new ArrayList<>();
|
||||
for (String s : dataList) {
|
||||
String data = s.trim();
|
||||
if (data.length() == 0) {
|
||||
continue; //跳过循环
|
||||
}
|
||||
if (dictCode.contains(",")) {
|
||||
String keyString = String.format("sys:cache:dictTable::SimpleKey [%s,%s]", dictCode, data);
|
||||
if (redisTemplate.hasKey(keyString)) {
|
||||
try {
|
||||
String text = oConvertUtils.getString(redisTemplate.opsForValue().get(keyString));
|
||||
List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
|
||||
list.add(new DictModel(data, text));
|
||||
} catch (Exception e) {
|
||||
log.warn(e.getMessage());
|
||||
}
|
||||
} else if (!needTranslDataTable.contains(data)) {
|
||||
// 去重添加
|
||||
needTranslDataTable.add(data);
|
||||
}
|
||||
} else {
|
||||
String keyString = String.format("sys:cache:dict::%s:%s", dictCode, data);
|
||||
if (redisTemplate.hasKey(keyString)) {
|
||||
try {
|
||||
String text = oConvertUtils.getString(redisTemplate.opsForValue().get(keyString));
|
||||
List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
|
||||
list.add(new DictModel(data, text));
|
||||
} catch (Exception e) {
|
||||
log.warn(e.getMessage());
|
||||
}
|
||||
} else if (!needTranslData.contains(data)) {
|
||||
// 去重添加
|
||||
needTranslData.add(data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//step.2 调用数据库翻译表字典
|
||||
if (needTranslDataTable.size() > 0) {
|
||||
String[] arr = dictCode.split(",");
|
||||
String table = arr[0], text = arr[1], code = arr[2];
|
||||
String values = String.join(",", needTranslDataTable);
|
||||
log.info("translateDictFromTableByKeys.dictCode:" + dictCode);
|
||||
log.info("translateDictFromTableByKeys.values:" + values);
|
||||
List<DictModel> texts = commonAPI.translateDictFromTableByKeys(table, text, code, values);
|
||||
log.info("translateDictFromTableByKeys.result:" + texts);
|
||||
List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
|
||||
list.addAll(texts);
|
||||
|
||||
// 做 redis 缓存
|
||||
for (DictModel dict : texts) {
|
||||
String redisKey = String.format("sys:cache:dictTable::SimpleKey [%s,%s]", dictCode, dict.getValue());
|
||||
try {
|
||||
redisTemplate.opsForValue().set(redisKey, dict.getText());
|
||||
} catch (Exception e) {
|
||||
log.warn(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//step.3 调用数据库进行翻译普通字典
|
||||
if (needTranslData.size() > 0) {
|
||||
List<String> dictCodeList = Arrays.asList(dataListMap.keySet().toArray(new String[]{}));
|
||||
// 将不包含逗号的字典code筛选出来,因为带逗号的是表字典,而不是普通的数据字典
|
||||
List<String> filterDictCodes = dictCodeList.stream().filter(key -> !key.contains(",")).collect(Collectors.toList());
|
||||
String dictCodes = String.join(",", filterDictCodes);
|
||||
String values = String.join(",", needTranslData);
|
||||
log.info("translateManyDict.dictCodes:" + dictCodes);
|
||||
log.info("translateManyDict.values:" + values);
|
||||
Map<String, List<DictModel>> manyDict = commonAPI.translateManyDict(dictCodes, values);
|
||||
log.info("translateManyDict.result:" + manyDict);
|
||||
for (String dictCode : manyDict.keySet()) {
|
||||
List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
|
||||
List<DictModel> newList = manyDict.get(dictCode);
|
||||
list.addAll(newList);
|
||||
|
||||
// 做 redis 缓存
|
||||
for (DictModel dict : newList) {
|
||||
String redisKey = String.format("sys:cache:dict::%s:%s", dictCode, dict.getValue());
|
||||
try {
|
||||
redisTemplate.opsForValue().set(redisKey, dict.getText());
|
||||
} catch (Exception e) {
|
||||
log.warn(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return translText;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字典值替换文本
|
||||
*
|
||||
* @param dictModels
|
||||
* @param values
|
||||
* @return
|
||||
*/
|
||||
private String translDictText(List<DictModel> dictModels, String values) {
|
||||
List<String> result = new ArrayList<>();
|
||||
|
||||
// 允许多个逗号分隔,允许传数组对象
|
||||
String[] splitVal = values.split(",");
|
||||
for (String val : splitVal) {
|
||||
String dictText = val;
|
||||
for (DictModel dict : dictModels) {
|
||||
if (val.equals(dict.getValue())) {
|
||||
dictText = dict.getText();
|
||||
break;
|
||||
}
|
||||
}
|
||||
result.add(dictText);
|
||||
}
|
||||
return String.join(",", result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 翻译字典文本
|
||||
* @param code
|
||||
@@ -135,6 +334,7 @@ public class DictAspect {
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
private String translateDictValue(String code, String text, String table, String key) {
|
||||
if(oConvertUtils.isEmpty(key)) {
|
||||
return null;
|
||||
@@ -186,3 +386,4 @@ public class DictAspect {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -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";
|
||||
|
||||
/**
|
||||
* 多租户 请求头
|
||||
|
||||
+24
@@ -26,6 +26,8 @@ public class JeroElasticsearchTemplate {
|
||||
/** es服务地址 */
|
||||
private String baseUrl;
|
||||
private final String FORMAT_JSON = "format=json";
|
||||
/** Elasticsearch 的版本号 */
|
||||
private String version = null;
|
||||
|
||||
// ElasticSearch 最大可返回条目数
|
||||
public static final int ES_MAX_SIZE = 10000;
|
||||
@@ -37,15 +39,32 @@ public class JeroElasticsearchTemplate {
|
||||
// 验证配置的ES地址是否有效
|
||||
if (checkEnabled) {
|
||||
try {
|
||||
this.getElasticsearchVersion();
|
||||
RestUtil.get(this.getBaseUrl().toString());
|
||||
log.info("ElasticSearch 服务连接成功");
|
||||
log.info("ElasticSearch version: " + this.version);
|
||||
} catch (Exception e) {
|
||||
this.version = "";
|
||||
log.warn("ElasticSearch 服务连接失败,原因:配置未通过。可能是BaseURL未配置或配置有误,也可能是Elasticsearch服务未启动。接下来将会拒绝执行任何方法!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Elasticsearch 的版本号信息,失败返回null
|
||||
*/
|
||||
private void getElasticsearchVersion() {
|
||||
if (this.version == null) {
|
||||
String url = this.getBaseUrl().toString();
|
||||
JSONObject result = RestUtil.get(url);
|
||||
if (result != null) {
|
||||
JSONObject v = result.getJSONObject("version");
|
||||
this.version = v.getString("number");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public StringBuilder getBaseUrl(String indexName, String typeName) {
|
||||
typeName = typeName.trim().toLowerCase();
|
||||
return this.getBaseUrl(indexName).append("/").append(typeName);
|
||||
@@ -185,6 +204,11 @@ public class JeroElasticsearchTemplate {
|
||||
*/
|
||||
public JSONObject getIndexMapping(String indexName, String typeName) {
|
||||
String url = this.getBaseUrl(indexName, typeName).append("/_mapping?").append(FORMAT_JSON).toString();
|
||||
// 针对 es 7.x 版本做兼容
|
||||
this.getElasticsearchVersion();
|
||||
if (oConvertUtils.isNotEmpty(this.version) && this.version.startsWith("7")) {
|
||||
url += "&include_type_name=true";
|
||||
}
|
||||
log.info("getIndexMapping-url:" + url);
|
||||
/*
|
||||
* 参考返回JSON结构:
|
||||
|
||||
+16
-2
@@ -5,9 +5,15 @@ import java.io.Serializable;
|
||||
public class QueryCondition implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 4740166316629191651L;
|
||||
|
||||
|
||||
private String field;
|
||||
/** 组件的类型(例如:input、select、radio) */
|
||||
private String type;
|
||||
/**
|
||||
* 对应的数据库字段的类型
|
||||
* 支持:int、bigDecimal、short、long、float、double、boolean
|
||||
*/
|
||||
private String dbType;
|
||||
private String rule;
|
||||
private String val;
|
||||
|
||||
@@ -27,6 +33,14 @@ public class QueryCondition implements Serializable {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getDbType() {
|
||||
return dbType;
|
||||
}
|
||||
|
||||
public void setDbType(String dbType) {
|
||||
this.dbType = dbType;
|
||||
}
|
||||
|
||||
public String getRule() {
|
||||
return rule;
|
||||
}
|
||||
@@ -49,7 +63,7 @@ public class QueryCondition implements Serializable {
|
||||
if(field == null || "".equals(field)){
|
||||
return "";
|
||||
}
|
||||
sb.append(this.field).append(" ").append(this.rule).append(" ").append(this.type).append(" ").append(this.val);
|
||||
sb.append(this.field).append(" ").append(this.rule).append(" ").append(this.type).append(" ").append(this.dbType).append(" ").append(this.val);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
+21
-18
@@ -1,21 +1,5 @@
|
||||
package com.jero.common.system.query;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.beanutils.PropertyUtils;
|
||||
import com.jero.common.constant.CommonConstant;
|
||||
import com.jero.common.constant.DataBaseConstant;
|
||||
import com.jero.common.system.util.JeroDataAutorUtils;
|
||||
import com.jero.common.system.util.JwtUtil;
|
||||
import com.jero.common.system.vo.SysPermissionDataRuleModel;
|
||||
import com.jero.common.util.CommonUtils;
|
||||
import com.jero.common.util.DateUtils;
|
||||
import com.jero.common.util.SqlInjectionUtil;
|
||||
import com.jero.common.util.oConvertUtils;
|
||||
import org.springframework.util.NumberUtils;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.reflect.Field;
|
||||
@@ -28,6 +12,25 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.beanutils.PropertyUtils;
|
||||
import com.jero.common.constant.CommonConstant;
|
||||
import com.jero.common.constant.DataBaseConstant;
|
||||
import com.jero.common.system.util.JeroDataAutorUtils;
|
||||
import com.jero.common.system.util.JwtUtil;
|
||||
import com.jero.common.system.vo.SysPermissionDataRuleModel;
|
||||
import com.jero.common.util.CommonUtils;
|
||||
import com.jero.common.util.DateUtils;
|
||||
import com.jero.common.util.SqlInjectionUtil;
|
||||
import com.jero.common.util.oConvertUtils;
|
||||
import org.springframework.util.NumberUtils;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class QueryGenerator {
|
||||
public static final String SQL_RULES_COLUMN = "SQL_RULES_COLUMN";
|
||||
@@ -238,12 +241,12 @@ public class QueryGenerator {
|
||||
// queryWrapper.orderByAsc(oConvertUtils.camelToUnderline(column));
|
||||
String columnStr = oConvertUtils.camelToUnderline(column);
|
||||
String[] columnArray = columnStr.split(",");
|
||||
queryWrapper.orderByAsc(columnArray);
|
||||
queryWrapper.orderByAsc(Arrays.asList(columnArray));
|
||||
} else {
|
||||
// queryWrapper.orderByDesc(oConvertUtils.camelToUnderline(column));
|
||||
String columnStr = oConvertUtils.camelToUnderline(column);
|
||||
String[] columnArray = columnStr.split(",");
|
||||
queryWrapper.orderByDesc(columnArray);
|
||||
queryWrapper.orderByDesc(Arrays.asList(columnArray));
|
||||
}
|
||||
//update-end--Author:scott Date:20210531 for:36 多条件排序无效问题修正-------
|
||||
}
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.jero.common.system.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 查询多个字典时用到
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class DictModelMany extends DictModel {
|
||||
|
||||
/**
|
||||
* 字典code,根据多个字段code查询时才用到,用于区分不同的字典选项
|
||||
*/
|
||||
private String dictCode;
|
||||
|
||||
}
|
||||
-4
@@ -36,10 +36,6 @@ public class DynamicDataSourceModel {
|
||||
* 数据源地址
|
||||
*/
|
||||
private java.lang.String dbUrl;
|
||||
/**
|
||||
* 数据库名称
|
||||
*/
|
||||
private java.lang.String dbName;
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
|
||||
+46
-15
@@ -1,30 +1,31 @@
|
||||
package com.jero.common.util;
|
||||
|
||||
import java.io.*;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import cn.hutool.crypto.asymmetric.KeyType;
|
||||
import cn.hutool.crypto.asymmetric.RSA;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import com.jero.common.constant.CommonConstant;
|
||||
import com.jero.common.constant.DataBaseConstant;
|
||||
import com.jero.common.exception.JeroBootException;
|
||||
import com.jero.common.util.filter.FileTypeFilter;
|
||||
import com.jero.common.util.oss.OssBootUtil;
|
||||
import org.jeecgframework.poi.util.PoiPublicUtil;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class CommonUtils {
|
||||
@@ -134,6 +135,9 @@ public class CommonUtils {
|
||||
*/
|
||||
public static String uploadLocal(MultipartFile mf,String bizPath, String uploadpath){
|
||||
try {
|
||||
//update-begin-author:liusq date:20210809 for: 过滤上传文件类型
|
||||
FileTypeFilter.fileTypeFilter(mf);
|
||||
//update-end-author:liusq date:20210809 for: 过滤上传文件类型
|
||||
String ctxPath = uploadpath;
|
||||
String fileName = null;
|
||||
File file = new File(ctxPath + File.separator + bizPath + File.separator );
|
||||
@@ -162,11 +166,20 @@ public class CommonUtils {
|
||||
return dbpath;
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
/** 当前系统数据库类型 */
|
||||
private static String DB_TYPE = "";
|
||||
private static DbType dbTypeEnum = null;
|
||||
|
||||
/**
|
||||
* 全局获取平台数据库类型(作废了)
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
public static String getDatabaseType() {
|
||||
if(oConvertUtils.isNotEmpty(DB_TYPE)){
|
||||
return DB_TYPE;
|
||||
@@ -181,6 +194,24 @@ public class CommonUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 全局获取平台数据库类型(对应mybaisPlus枚举)
|
||||
* @return
|
||||
*/
|
||||
public static DbType getDatabaseTypeEnum() {
|
||||
if (oConvertUtils.isNotEmpty(dbTypeEnum)) {
|
||||
return dbTypeEnum;
|
||||
}
|
||||
try {
|
||||
DataSource dataSource = SpringContextUtils.getApplicationContext().getBean(DataSource.class);
|
||||
dbTypeEnum = JdbcUtils.getDbType(dataSource.getConnection().getMetaData().getURL());
|
||||
return dbTypeEnum;
|
||||
} catch (SQLException e) {
|
||||
log.warn(e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库类型
|
||||
* @param dataSource
|
||||
|
||||
+4
@@ -621,6 +621,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类型的时间值
|
||||
|
||||
+4
-1
@@ -1,11 +1,11 @@
|
||||
package com.jero.common.util;
|
||||
|
||||
import com.jero.common.util.filter.FileTypeFilter;
|
||||
import io.minio.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import com.jero.common.util.filter.StrAttackFilter;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.validation.constraints.Null;
|
||||
import java.io.InputStream;
|
||||
import java.net.URLDecoder;
|
||||
|
||||
@@ -69,6 +69,9 @@ public class MinioUtil {
|
||||
minioClient.makeBucket(MakeBucketArgs.builder().bucket(newBucket).build());
|
||||
log.info("create a new bucket.");
|
||||
}
|
||||
//update-begin-author:liusq date:20210809 for: 过滤上传文件类型
|
||||
FileTypeFilter.fileTypeFilter(file);
|
||||
//update-end-author:liusq date:20210809 for: 过滤上传文件类型
|
||||
InputStream stream = file.getInputStream();
|
||||
// 获取文件名
|
||||
String orgName = file.getOriginalFilename();
|
||||
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
package com.jero.common.util;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
|
||||
/**
|
||||
* 使用Spring自身提供的地址匹配工具匹配URL
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
+17
@@ -192,6 +192,23 @@ public class ReflectHelper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取属性值
|
||||
*/
|
||||
public static Object getFieldVal(String fieldName, Object o) {
|
||||
try {
|
||||
// 暴力反射获取属性
|
||||
Field filed = o.getClass().getDeclaredField(fieldName);
|
||||
// 设置反射时取消Java的访问检查,暴力访问
|
||||
filed.setAccessible(true);
|
||||
Object val = filed.get(o);
|
||||
return val;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取属性名数组
|
||||
*/
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.jero.common.util.dynamic.db;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
|
||||
/**
|
||||
* 数据库类型判断
|
||||
* 【有些数据库引擎是一样的,以达到复用目的】
|
||||
*/
|
||||
public class DbTypeUtils {
|
||||
|
||||
public static boolean dbTypeIsMySQL(DbType dbType) {
|
||||
return dbTypeIf(dbType, DbType.MYSQL, DbType.MARIADB, DbType.CLICK_HOUSE, DbType.SQLITE);
|
||||
}
|
||||
|
||||
public static boolean dbTypeIsOracle(DbType dbType) {
|
||||
return dbTypeIf(dbType, DbType.ORACLE, DbType.ORACLE_12C, DbType.DM);
|
||||
}
|
||||
|
||||
public static boolean dbTypeIsSQLServer(DbType dbType) {
|
||||
return dbTypeIf(dbType, DbType.SQL_SERVER, DbType.SQL_SERVER2005);
|
||||
}
|
||||
|
||||
public static boolean dbTypeIsPostgre(DbType dbType) {
|
||||
return dbTypeIf(dbType, DbType.POSTGRE_SQL, DbType.KINGBASE_ES, DbType.GAUSS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断数据库类型
|
||||
*/
|
||||
public static boolean dbTypeIf(DbType dbType, DbType... correctTypes) {
|
||||
for (DbType type : correctTypes) {
|
||||
if (type.equals(dbType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
+2
-1
@@ -49,7 +49,7 @@ public class DynamicDBUtil {
|
||||
dataSource.setBreakAfterAcquireFailure(true);
|
||||
dataSource.setConnectionErrorRetryAttempts(0);
|
||||
dataSource.setUsername(dbUser);
|
||||
dataSource.setMaxWait(60000);
|
||||
dataSource.setMaxWait(30000);
|
||||
dataSource.setPassword(dbPassword);
|
||||
|
||||
log.info("******************************************");
|
||||
@@ -151,6 +151,7 @@ public class DynamicDBUtil {
|
||||
list = findList(dbKey, sql, param);
|
||||
if (oConvertUtils.listIsEmpty(list)) {
|
||||
log.error("Except one, but not find actually");
|
||||
return null;
|
||||
}
|
||||
if (list.size() > 1) {
|
||||
log.error("Except one, but more than one actually");
|
||||
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
package com.jero.common.util.filter;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* @Description: TODO
|
||||
* @author: lsq
|
||||
* @date: 2021年08月09日 15:29
|
||||
*/
|
||||
public class FileTypeFilter {
|
||||
|
||||
//文件后缀
|
||||
private static String[] forbidType = {"jsp","php"};
|
||||
|
||||
// 初始化文件头类型,不够的自行补充
|
||||
final static HashMap<String, String> fileTypeMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
fileTypeMap.put("3c25402070616765206c", "jsp");
|
||||
fileTypeMap.put("3c3f7068700a0a2f2a2a0a202a205048", "php");
|
||||
/* fileTypeMap.put("ffd8ffe000104a464946", "jpg");
|
||||
fileTypeMap.put("89504e470d0a1a0a0000", "png");
|
||||
fileTypeMap.put("47494638396126026f01", "gif");
|
||||
fileTypeMap.put("49492a00227105008037", "tif");
|
||||
fileTypeMap.put("424d228c010000000000", "bmp");
|
||||
fileTypeMap.put("424d8240090000000000", "bmp");
|
||||
fileTypeMap.put("424d8e1b030000000000", "bmp");
|
||||
fileTypeMap.put("41433130313500000000", "dwg");
|
||||
fileTypeMap.put("3c21444f435459504520", "html");
|
||||
fileTypeMap.put("3c21646f637479706520", "htm");
|
||||
fileTypeMap.put("48544d4c207b0d0a0942", "css");
|
||||
fileTypeMap.put("696b2e71623d696b2e71", "js");
|
||||
fileTypeMap.put("7b5c727466315c616e73", "rtf");
|
||||
fileTypeMap.put("38425053000100000000", "psd");
|
||||
fileTypeMap.put("46726f6d3a203d3f6762", "eml");
|
||||
fileTypeMap.put("d0cf11e0a1b11ae10000", "doc");
|
||||
fileTypeMap.put("5374616E64617264204A", "mdb");
|
||||
fileTypeMap.put("252150532D41646F6265", "ps");
|
||||
fileTypeMap.put("255044462d312e350d0a", "pdf");
|
||||
fileTypeMap.put("2e524d46000000120001", "rmvb");
|
||||
fileTypeMap.put("464c5601050000000900", "flv");
|
||||
fileTypeMap.put("00000020667479706d70", "mp4");
|
||||
fileTypeMap.put("49443303000000002176", "mp3");
|
||||
fileTypeMap.put("000001ba210001000180", "mpg");
|
||||
fileTypeMap.put("3026b2758e66cf11a6d9", "wmv");
|
||||
fileTypeMap.put("52494646e27807005741", "wav");
|
||||
fileTypeMap.put("52494646d07d60074156", "avi");
|
||||
fileTypeMap.put("4d546864000000060001", "mid");
|
||||
fileTypeMap.put("504b0304140000000800", "zip");
|
||||
fileTypeMap.put("526172211a0700cf9073", "rar");
|
||||
fileTypeMap.put("235468697320636f6e66", "ini");
|
||||
fileTypeMap.put("504b03040a0000000000", "jar");
|
||||
fileTypeMap.put("4d5a9000030000000400", "exe");
|
||||
fileTypeMap.put("3c25402070616765206c", "jsp");
|
||||
fileTypeMap.put("4d616e69666573742d56", "mf");
|
||||
fileTypeMap.put("3c3f786d6c2076657273", "xml");
|
||||
fileTypeMap.put("494e5345525420494e54", "sql");
|
||||
fileTypeMap.put("7061636b616765207765", "java");
|
||||
fileTypeMap.put("406563686f206f66660d", "bat");
|
||||
fileTypeMap.put("1f8b0800000000000000", "gz");
|
||||
fileTypeMap.put("6c6f67346a2e726f6f74", "properties");
|
||||
fileTypeMap.put("cafebabe0000002e0041", "class");
|
||||
fileTypeMap.put("49545346030000006000", "chm");
|
||||
fileTypeMap.put("04000000010000001300", "mxp");
|
||||
fileTypeMap.put("504b0304140006000800", "docx");
|
||||
fileTypeMap.put("6431303a637265617465", "torrent");
|
||||
fileTypeMap.put("6D6F6F76", "mov");
|
||||
fileTypeMap.put("FF575043", "wpd");
|
||||
fileTypeMap.put("CFAD12FEC5FD746F", "dbx");
|
||||
fileTypeMap.put("2142444E", "pst");
|
||||
fileTypeMap.put("AC9EBD8F", "qdf");
|
||||
fileTypeMap.put("E3828596", "pwl");
|
||||
fileTypeMap.put("2E7261FD", "ram");*/
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileName
|
||||
* @return String
|
||||
* @description 通过文件后缀名获取文件类型
|
||||
*/
|
||||
private static String getFileTypeBySuffix(String fileName) {
|
||||
return fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件类型过滤
|
||||
*
|
||||
* @param file
|
||||
*/
|
||||
public static void fileTypeFilter(MultipartFile file) throws Exception {
|
||||
String suffix = getFileType(file);
|
||||
for (String type : forbidType) {
|
||||
if (type.contains(suffix)) {
|
||||
throw new Exception("上传失败,文件类型异常:" + suffix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过读取文件头部获得文件类型
|
||||
*
|
||||
* @param file
|
||||
* @return 文件类型
|
||||
* @throws Exception
|
||||
*/
|
||||
|
||||
private static String getFileType(MultipartFile file) throws Exception {
|
||||
String fileExtendName = null;
|
||||
InputStream is;
|
||||
try {
|
||||
//is = new FileInputStream(file);
|
||||
is = file.getInputStream();
|
||||
byte[] b = new byte[10];
|
||||
is.read(b, 0, b.length);
|
||||
String fileTypeHex = String.valueOf(bytesToHexString(b));
|
||||
Iterator<String> keyIter = fileTypeMap.keySet().iterator();
|
||||
while (keyIter.hasNext()) {
|
||||
String key = keyIter.next();
|
||||
// 验证前5个字符比较
|
||||
if (key.toLowerCase().startsWith(fileTypeHex.toLowerCase().substring(0, 5))
|
||||
|| fileTypeHex.toLowerCase().substring(0, 5).startsWith(key.toLowerCase())) {
|
||||
fileExtendName = fileTypeMap.get(key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 如果不是上述类型,则判断扩展名
|
||||
if (StringUtils.isBlank(fileExtendName)) {
|
||||
String fileName = file.getOriginalFilename();
|
||||
return getFileTypeBySuffix(fileName);
|
||||
}
|
||||
is.close();
|
||||
return fileExtendName;
|
||||
} catch (Exception exception) {
|
||||
throw new Exception(exception.getMessage(), exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得文件头部字符串
|
||||
*
|
||||
* @param src
|
||||
* @return
|
||||
*/
|
||||
private static String bytesToHexString(byte[] src) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
if (src == null || src.length <= 0) {
|
||||
return null;
|
||||
}
|
||||
for (int i = 0; i < src.length; i++) {
|
||||
int v = src[i] & 0xFF;
|
||||
String hv = Integer.toHexString(v);
|
||||
if (hv.length() < 2) {
|
||||
stringBuilder.append(0);
|
||||
}
|
||||
stringBuilder.append(hv);
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
}
|
||||
+7
@@ -6,6 +6,7 @@ import com.aliyun.oss.common.auth.DefaultCredentialProvider;
|
||||
import com.aliyun.oss.model.CannedAccessControlList;
|
||||
import com.aliyun.oss.model.OSSObject;
|
||||
import com.aliyun.oss.model.PutObjectResult;
|
||||
import com.jero.common.util.filter.FileTypeFilter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.tomcat.util.http.fileupload.FileItemStream;
|
||||
import com.jero.common.util.CommonUtils;
|
||||
@@ -110,6 +111,9 @@ public class OssBootUtil {
|
||||
if("" == orgName){
|
||||
orgName=file.getName();
|
||||
}
|
||||
//update-begin-author:liusq date:20210809 for: 过滤上传文件类型
|
||||
FileTypeFilter.fileTypeFilter(file);
|
||||
//update-end-author:liusq date:20210809 for: 过滤上传文件类型
|
||||
orgName = CommonUtils.getFileName(orgName);
|
||||
String fileName = orgName.indexOf(".")==-1
|
||||
?orgName + "_" + System.currentTimeMillis()
|
||||
@@ -136,6 +140,9 @@ public class OssBootUtil {
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return FILE_URL;
|
||||
}
|
||||
|
||||
+2
-3
@@ -8,9 +8,8 @@ import cn.hutool.crypto.asymmetric.RSA;
|
||||
import cn.hutool.crypto.asymmetric.Sign;
|
||||
import cn.hutool.crypto.asymmetric.SignAlgorithm;
|
||||
import cn.hutool.crypto.symmetric.AES;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.jero.common.util.security.entity.*;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import javax.crypto.SecretKey;
|
||||
import java.security.KeyPair;
|
||||
|
||||
@@ -34,7 +33,7 @@ public class SecurityTools {
|
||||
|
||||
String dencrptValue =aes.decryptStr(data);
|
||||
//log.info("解密后报文"+dencrptValue);
|
||||
resp.setData(new JSONObject(dencrptValue));
|
||||
resp.setData(JSONObject.parseObject(dencrptValue));
|
||||
|
||||
boolean verify = sign.verify(dencrptValue.getBytes(), Base64Decoder.decode(signData));
|
||||
resp.setSuccess(verify);
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package com.jero.common.util.security.entity;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
|
||||
+7
-5
@@ -1,8 +1,9 @@
|
||||
package com.jero.config;
|
||||
|
||||
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
|
||||
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
|
||||
import com.alibaba.druid.util.Utils;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.*;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
@@ -10,8 +11,9 @@ import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.servlet.*;
|
||||
import java.io.IOException;
|
||||
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
|
||||
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
|
||||
import com.alibaba.druid.util.Utils;
|
||||
|
||||
@Configuration
|
||||
@AutoConfigureAfter(DruidDataSourceAutoConfigure.class)
|
||||
|
||||
+5
@@ -20,6 +20,11 @@ public class StaticConfig {
|
||||
@Value(value = "${spring.mail.username}")
|
||||
private String emailFrom;
|
||||
|
||||
/**
|
||||
* 签名密钥串
|
||||
*/
|
||||
@Value(value = "${jero.signatureSecret}")
|
||||
private String signatureSecret;
|
||||
|
||||
/*@Bean
|
||||
public void initStatic() {
|
||||
|
||||
-130
@@ -1,130 +0,0 @@
|
||||
package com.jero.config.mybatis;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantSqlParser;
|
||||
import net.sf.jsqlparser.expression.BinaryExpression;
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
import net.sf.jsqlparser.expression.Parenthesis;
|
||||
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
|
||||
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
|
||||
import net.sf.jsqlparser.expression.operators.relational.*;
|
||||
import net.sf.jsqlparser.schema.Column;
|
||||
import net.sf.jsqlparser.schema.Table;
|
||||
import net.sf.jsqlparser.statement.select.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 复写租户条件
|
||||
*/
|
||||
public class JeroTenantParser extends TenantSqlParser {
|
||||
|
||||
/**
|
||||
* @param expression
|
||||
* @param table
|
||||
* @return
|
||||
*/
|
||||
protected Expression processTableAlias(Expression expression, Table table) {
|
||||
String tableAliasName;
|
||||
if (table.getAlias() == null) {
|
||||
tableAliasName = table.getName();
|
||||
} else {
|
||||
tableAliasName = table.getAlias().getName();
|
||||
}
|
||||
|
||||
// in
|
||||
if (expression instanceof InExpression) {
|
||||
InExpression in = (InExpression) expression;
|
||||
if (in.getLeftExpression() instanceof Column) {
|
||||
setTableAliasNameForColumn((Column) in.getLeftExpression(), tableAliasName);
|
||||
}
|
||||
|
||||
// 比较操作
|
||||
} else if (expression instanceof BinaryExpression) {
|
||||
BinaryExpression compare = (BinaryExpression) expression;
|
||||
if (compare.getLeftExpression() instanceof Column) {
|
||||
setTableAliasNameForColumn((Column) compare.getLeftExpression(), tableAliasName);
|
||||
} else if (compare.getRightExpression() instanceof Column) {
|
||||
setTableAliasNameForColumn((Column) compare.getRightExpression(), tableAliasName);
|
||||
}
|
||||
|
||||
// between
|
||||
} else if (expression instanceof Between) {
|
||||
Between between = (Between) expression;
|
||||
if (between.getLeftExpression() instanceof Column) {
|
||||
setTableAliasNameForColumn((Column) between.getLeftExpression(), tableAliasName);
|
||||
}
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
|
||||
private void setTableAliasNameForColumn(Column column, String tableAliasName) {
|
||||
column.setColumnName(tableAliasName + "." + column.getColumnName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认是按 tenant_id=1 按等于条件追加
|
||||
*
|
||||
* @param currentExpression 现有的条件:比如你原来的sql查询条件
|
||||
* @param table
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected Expression builderExpression(Expression currentExpression, Table table) {
|
||||
final Expression tenantExpression = this.getTenantHandler().getTenantId(true);
|
||||
Expression appendExpression;
|
||||
if (!(tenantExpression instanceof SupportsOldOracleJoinSyntax)) {
|
||||
appendExpression = new EqualsTo();
|
||||
((EqualsTo) appendExpression).setLeftExpression(this.getAliasColumn(table));
|
||||
((EqualsTo) appendExpression).setRightExpression(tenantExpression);
|
||||
} else {
|
||||
appendExpression = processTableAlias(tenantExpression, table);
|
||||
}
|
||||
if (currentExpression == null) {
|
||||
return appendExpression;
|
||||
}
|
||||
if (currentExpression instanceof BinaryExpression) {
|
||||
BinaryExpression binaryExpression = (BinaryExpression) currentExpression;
|
||||
if (binaryExpression.getLeftExpression() instanceof FromItem) {
|
||||
processFromItem((FromItem) binaryExpression.getLeftExpression());
|
||||
}
|
||||
if (binaryExpression.getRightExpression() instanceof FromItem) {
|
||||
processFromItem((FromItem) binaryExpression.getRightExpression());
|
||||
}
|
||||
} else if (currentExpression instanceof InExpression) {
|
||||
InExpression inExp = (InExpression) currentExpression;
|
||||
ItemsList rightItems = inExp.getRightItemsList();
|
||||
if (rightItems instanceof SubSelect) {
|
||||
processSelectBody(((SubSelect) rightItems).getSelectBody());
|
||||
}
|
||||
}
|
||||
if (currentExpression instanceof OrExpression) {
|
||||
return new AndExpression(new Parenthesis(currentExpression), appendExpression);
|
||||
} else {
|
||||
return new AndExpression(currentExpression, appendExpression);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processPlainSelect(PlainSelect plainSelect, boolean addColumn) {
|
||||
FromItem fromItem = plainSelect.getFromItem();
|
||||
if (fromItem instanceof Table) {
|
||||
Table fromTable = (Table) fromItem;
|
||||
if (!this.getTenantHandler().doTableFilter(fromTable.getName())) {
|
||||
plainSelect.setWhere(builderExpression(plainSelect.getWhere(), fromTable));
|
||||
if (addColumn) {
|
||||
plainSelect.getSelectItems().add(new SelectExpressionItem(new Column(this.getTenantHandler().getTenantIdColumn())));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
processFromItem(fromItem);
|
||||
}
|
||||
List<Join> joins = plainSelect.getJoins();
|
||||
if (joins != null && joins.size() > 0) {
|
||||
joins.forEach(j -> {
|
||||
processJoin(j);
|
||||
processFromItem(j.getRightItem());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-142
@@ -1,142 +0,0 @@
|
||||
package com.jero.config.mybatis;
|
||||
|
||||
import com.baomidou.mybatisplus.core.parser.ISqlParser;
|
||||
import com.baomidou.mybatisplus.core.parser.ISqlParserFilter;
|
||||
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
|
||||
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler;
|
||||
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantSqlParser;
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
import net.sf.jsqlparser.expression.LongValue;
|
||||
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
|
||||
import net.sf.jsqlparser.expression.operators.relational.InExpression;
|
||||
import net.sf.jsqlparser.schema.Column;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import com.jero.common.util.oConvertUtils;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 单数据源配置(jero.datasource.open = false时生效)
|
||||
* @Author zhoujf
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
@MapperScan(value={"com.jero.**.mapper*"})
|
||||
public class MybatisPlusConfig {
|
||||
|
||||
/**
|
||||
* tenant_id 字段名
|
||||
*/
|
||||
public static final String tenant_field = "tenant_id";
|
||||
|
||||
/**
|
||||
* 有哪些表需要做多租户 这些表需要添加一个字段 ,字段名和tenant_field对应的值一样
|
||||
*/
|
||||
private static final List<String> tenantTable = new ArrayList<String>();
|
||||
/**
|
||||
* ddl 关键字 判断不走多租户的sql过滤
|
||||
*/
|
||||
private static final List<String> DDL_KEYWORD = new ArrayList<String>();
|
||||
static {
|
||||
tenantTable.add("jee_bug_danbiao");
|
||||
DDL_KEYWORD.add("alter");
|
||||
}
|
||||
|
||||
/**
|
||||
* 多租户属于 SQL 解析部分,依赖 MP 分页插件
|
||||
*/
|
||||
@Bean
|
||||
public PaginationInterceptor paginationInterceptor() {
|
||||
PaginationInterceptor paginationInterceptor = new PaginationInterceptor().setLimit(-1);
|
||||
//多租户配置 配置后每次执行sql会走一遍他的转化器 如果不需要多租户功能 可以将其注释
|
||||
tenantConfig(paginationInterceptor);
|
||||
return paginationInterceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 多租户的配置
|
||||
* @param paginationInterceptor
|
||||
*/
|
||||
private void tenantConfig(PaginationInterceptor paginationInterceptor){
|
||||
/*
|
||||
* 【测试多租户】 SQL 解析处理拦截器<br>
|
||||
* 这里固定写成住户 1 实际情况你可以从cookie读取,因此数据看不到 【 麻花藤 】 这条记录( 注意观察 SQL )<br>
|
||||
*/
|
||||
List<ISqlParser> sqlParserList = new ArrayList<>();
|
||||
TenantSqlParser tenantSqlParser = new JeroTenantParser();
|
||||
tenantSqlParser.setTenantHandler(new TenantHandler() {
|
||||
|
||||
@Override
|
||||
public Expression getTenantId(boolean select) {
|
||||
String tenant_id = oConvertUtils.getString(TenantContext.getTenant(),"0");
|
||||
return new LongValue(tenant_id);
|
||||
}
|
||||
@Override
|
||||
public String getTenantIdColumn() {
|
||||
return tenant_field;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doTableFilter(String tableName) {
|
||||
//true则不加租户条件查询 false则加
|
||||
// return excludeTable.contains(tableName);
|
||||
if(tenantTable.contains(tableName)){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private Expression in(String ids){
|
||||
final InExpression inExpression = new InExpression();
|
||||
inExpression.setLeftExpression(new Column(getTenantIdColumn()));
|
||||
final ExpressionList itemsList = new ExpressionList();
|
||||
final List<Expression> inValues = new ArrayList<>(2);
|
||||
for(String id:ids.split(",")){
|
||||
inValues.add(new LongValue(id));
|
||||
}
|
||||
itemsList.setExpressions(inValues);
|
||||
inExpression.setRightItemsList(itemsList);
|
||||
return inExpression;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
sqlParserList.add(tenantSqlParser);
|
||||
paginationInterceptor.setSqlParserList(sqlParserList);
|
||||
paginationInterceptor.setSqlParserFilter(new ISqlParserFilter() {
|
||||
@Override
|
||||
public boolean doFilter(MetaObject metaObject) {
|
||||
String sql = (String) metaObject.getValue(PluginUtils.DELEGATE_BOUNDSQL_SQL);
|
||||
for(String tableName: tenantTable){
|
||||
String sql_lowercase = sql.toLowerCase();
|
||||
if(sql_lowercase.indexOf(tableName.toLowerCase())>=0){
|
||||
for(String key: DDL_KEYWORD){
|
||||
if(sql_lowercase.indexOf(key)>=0){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/*if ("mapper路径.方法名".equals(ms.getId())) {
|
||||
//使用这种判断也可以避免走此过滤器
|
||||
return true;
|
||||
}*/
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
// /**
|
||||
// * mybatis-plus SQL执行效率插件【生产环境可以关闭】
|
||||
// */
|
||||
// @Bean
|
||||
// public PerformanceInterceptor performanceInterceptor() {
|
||||
// return new PerformanceInterceptor();
|
||||
// }
|
||||
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
package com.jero.config.mybatis;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
import net.sf.jsqlparser.expression.LongValue;
|
||||
import com.jero.common.util.oConvertUtils;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 单数据源配置(jeecg.datasource.open = false时生效)
|
||||
* @Author zhoujf
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
@MapperScan(value={"com.jero.modules.**.mapper*"})
|
||||
public class MybatisPlusSaasConfig {
|
||||
/**
|
||||
* tenant_id 字段名
|
||||
*/
|
||||
private static final String TENANT_FIELD_NAME = "tenant_id";
|
||||
/**
|
||||
* 哪些表需要做多租户 表需要添加一个字段 tenant_id
|
||||
*/
|
||||
private static final List<String> tenantTable = new ArrayList<String>();
|
||||
|
||||
static {
|
||||
tenantTable.add("demo");
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
// 先 add TenantLineInnerInterceptor 再 add PaginationInnerInterceptor
|
||||
interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {
|
||||
@Override
|
||||
public Expression getTenantId() {
|
||||
String tenant_id = oConvertUtils.getString(TenantContext.getTenant(),"0");
|
||||
return new LongValue(tenant_id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTenantIdColumn(){
|
||||
return TENANT_FIELD_NAME;
|
||||
}
|
||||
|
||||
// 返回 true 表示不走租户逻辑
|
||||
@Override
|
||||
public boolean ignoreTable(String tableName) {
|
||||
for(String temp: tenantTable){
|
||||
if(temp.equalsIgnoreCase(tableName)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}));
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -84,7 +84,6 @@ public class ShiroConfig {
|
||||
filterChainDefinitionMap.put("/sys/phoneLogin", "anon");//手机登录
|
||||
filterChainDefinitionMap.put("/sys/user/checkOnlyUser", "anon");//校验用户是否存在
|
||||
filterChainDefinitionMap.put("/sys/user/register", "anon");//用户注册
|
||||
filterChainDefinitionMap.put("/sys/user/querySysUser", "anon");//根据手机号获取用户信息
|
||||
filterChainDefinitionMap.put("/sys/user/phoneVerification", "anon");//用户忘记密码验证手机号
|
||||
filterChainDefinitionMap.put("/sys/user/passwordChange", "anon");//用户更改密码
|
||||
filterChainDefinitionMap.put("/auth/2step-code", "anon");//登录验证码
|
||||
@@ -102,6 +101,7 @@ public class ShiroConfig {
|
||||
filterChainDefinitionMap.put("/**/*.png", "anon");
|
||||
filterChainDefinitionMap.put("/**/*.ico", "anon");
|
||||
|
||||
//排除字体格式的后缀
|
||||
filterChainDefinitionMap.put("/**/*.ttf", "anon");
|
||||
filterChainDefinitionMap.put("/**/*.woff", "anon");
|
||||
filterChainDefinitionMap.put("/**/*.woff2", "anon");
|
||||
@@ -135,7 +135,7 @@ public class ShiroConfig {
|
||||
|
||||
// 添加自己的过滤器并且取名为jwt
|
||||
Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
|
||||
//如果cloudServer为空 则说明是单体 需要加载跨域配置
|
||||
//如果cloudServer为空 则说明是单体 需要加载跨域配置【微服务跨域切换】
|
||||
Object cloudServer = env.getProperty(CommonConstant.CLOUD_SERVER_KEY);
|
||||
filterMap.put("jwt", new JwtFilter(cloudServer==null));
|
||||
shiroFilterFactoryBean.setFilters(filterMap);
|
||||
|
||||
+12
-1
@@ -18,6 +18,7 @@ import com.jero.common.system.vo.LoginUser;
|
||||
import com.jero.common.util.RedisUtil;
|
||||
import com.jero.common.util.SpringContextUtils;
|
||||
import com.jero.common.util.oConvertUtils;
|
||||
import com.jero.config.mybatis.TenantContext;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -126,7 +127,17 @@ public class ShiroRealm extends AuthorizingRealm {
|
||||
if (!jwtTokenRefresh(token, username, loginUser.getPassword())) {
|
||||
throw new AuthenticationException("Token失效,请重新登录!");
|
||||
}
|
||||
|
||||
//update-begin-author:taoyan date:20210609 for:校验用户的tenant_id和前端传过来的是否一致
|
||||
String userTenantIds = loginUser.getRelTenantIds();
|
||||
if(oConvertUtils.isNotEmpty(userTenantIds)){
|
||||
String contextTenantId = TenantContext.getTenant();
|
||||
if(oConvertUtils.isNotEmpty(contextTenantId) && !"0".equals(contextTenantId)){
|
||||
if(String.join(",",userTenantIds).indexOf(contextTenantId)<0){
|
||||
throw new AuthenticationException("用户租户信息变更,请重新登陆!");
|
||||
}
|
||||
}
|
||||
}
|
||||
//update-end-author:taoyan date:20210609 for:校验用户的tenant_id和前端传过来的是否一致
|
||||
return loginUser;
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -1,5 +1,6 @@
|
||||
package com.jero.config.shiro.filters;
|
||||
|
||||
import com.jero.common.util.oConvertUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.shiro.authc.AuthenticationException;
|
||||
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
|
||||
@@ -22,6 +23,10 @@ import javax.servlet.http.HttpServletResponse;
|
||||
@Slf4j
|
||||
public class JwtFilter extends BasicHttpAuthenticationFilter {
|
||||
|
||||
/**
|
||||
* 默认开启跨域设置(使用单体)
|
||||
* 微服务情况下,此属性设置为false
|
||||
*/
|
||||
private boolean allowOrigin = true;
|
||||
|
||||
public JwtFilter(){}
|
||||
@@ -55,7 +60,7 @@ public class JwtFilter extends BasicHttpAuthenticationFilter {
|
||||
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
|
||||
String token = httpServletRequest.getHeader(CommonConstant.X_ACCESS_TOKEN);
|
||||
// update-begin--Author:lvdandan Date:20210105 for:JT-355 OA聊天添加token验证,获取token参数
|
||||
if(token == null){
|
||||
if (oConvertUtils.isEmpty(token)) {
|
||||
token = httpServletRequest.getParameter("token");
|
||||
}
|
||||
// update-end--Author:lvdandan Date:20210105 for:JT-355 OA聊天添加token验证,获取token参数
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.jero.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;
|
||||
|
||||
/**
|
||||
* 签名 拦截器配置
|
||||
*/
|
||||
@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();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(signAuthInterceptor()).addPathPatterns(urlList);
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
package com.jero.config.sign.interceptor;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.SortedMap;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.jero.common.api.vo.Result;
|
||||
import com.jero.common.constant.CommonConstant;
|
||||
import com.jero.common.util.DateUtils;
|
||||
import com.jero.config.sign.util.BodyReaderHttpServletRequestWrapper;
|
||||
import com.jero.config.sign.util.HttpUtils;
|
||||
import com.jero.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.info("request URI = " + request.getRequestURI());
|
||||
HttpServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(request);
|
||||
//获取全部参数(包括URL和body上的)
|
||||
SortedMap<String, String> allParams = HttpUtils.getAllParams(requestWrapper);
|
||||
//对参数进行签名验证
|
||||
String headerSign = request.getHeader(CommonConstant.X_SIGN);
|
||||
String timesTamp = request.getHeader(CommonConstant.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);
|
||||
//校验失败返回前端
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
package com.jero.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 jero
|
||||
* @date 20210621
|
||||
*/
|
||||
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) {
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
package com.jero.config.sign.util;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import com.jero.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;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* http 工具类 获取请求中的参数
|
||||
*
|
||||
* @author jeecg
|
||||
* @date 20210621
|
||||
*/
|
||||
@Slf4j
|
||||
public class HttpUtils {
|
||||
|
||||
/**
|
||||
* 将URL的参数和body参数合并
|
||||
*
|
||||
* @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(",")) {
|
||||
log.info(" pathVariable: {}",pathVariable);
|
||||
String deString = URLDecoder.decode(pathVariable, "UTF-8");
|
||||
log.info(" pathVariable decode: {}",deString);
|
||||
result.put(SignUtil.xPathVariable, deString);
|
||||
}
|
||||
// 获取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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将URL的参数和body参数合并
|
||||
*
|
||||
* @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(",")) {
|
||||
log.info(" pathVariable: {}",pathVariable);
|
||||
String deString = URLDecoder.decode(pathVariable, "UTF-8");
|
||||
log.info(" pathVariable decode: {}",deString);
|
||||
result.put(SignUtil.xPathVariable, deString);
|
||||
}
|
||||
// 获取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 参数
|
||||
*
|
||||
* @date 15:04 20210621
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将URL请求参数转换成Map
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将URL请求参数转换成Map
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package com.jero.config.sign.util;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import com.jero.common.exception.JeroBootException;
|
||||
import com.jero.common.util.SpringContextUtils;
|
||||
import com.jero.common.util.oConvertUtils;
|
||||
import com.jero.config.StaticConfig;
|
||||
import org.springframework.util.DigestUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.SortedMap;
|
||||
|
||||
/**
|
||||
* 签名工具类
|
||||
*
|
||||
* @author jeecg
|
||||
* @date 20210621
|
||||
*/
|
||||
@Slf4j
|
||||
public class SignUtil {
|
||||
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);
|
||||
StaticConfig staticConfig = SpringContextUtils.getBean(StaticConfig.class);
|
||||
String signatureSecret = staticConfig.getSignatureSecret();
|
||||
if(oConvertUtils.isEmpty(signatureSecret) || signatureSecret.contains("${")){
|
||||
throw new JeroBootException("签名密钥 ${jeecg.signatureSecret} 缺少配置 !!");
|
||||
}
|
||||
return DigestUtils.md5DigestAsHex((paramsJsonStr + signatureSecret).getBytes()).toUpperCase();
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
package com.jero.modules.base.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.SqlParser;
|
||||
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.jero.common.api.dto.LogDTO;
|
||||
|
||||
@@ -10,7 +10,7 @@ public interface BaseCommonMapper {
|
||||
* 保存日志
|
||||
* @param dto
|
||||
*/
|
||||
@SqlParser(filter=true)
|
||||
@InterceptorIgnore
|
||||
void saveLog(@Param("dto")LogDTO dto);
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user