jeecgboot3.4.2版本发布

This commit is contained in:
zhangdaiscott
2022-09-22 15:51:20 +08:00
parent 8dc91c33e5
commit 0ee38b7f4a
92 changed files with 1503 additions and 186 deletions

View File

@@ -146,11 +146,6 @@
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
</exclusion>
<!-- [issues/3596] 解决启动报错Cannot resolve com.sun:tools:1.8.0 -->
<exclusion>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
</exclusion>
</exclusions>
</dependency>
@@ -187,11 +182,6 @@
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<!-- 阿里云短信 -->
<dependency>

View File

@@ -0,0 +1,36 @@
package org.jeecg.common.api.dto;
import lombok.Data;
/**
* @Author taoYan
* @Date 2022/7/26 14:44
**/
@Data
public class DataLogDTO {
private String tableName;
private String dataId;
private String content;
private String type;
public DataLogDTO(){
}
public DataLogDTO(String tableName, String dataId, String content, String type) {
this.tableName = tableName;
this.dataId = dataId;
this.content = content;
this.type = type;
}
public DataLogDTO(String tableName, String dataId, String type) {
this.tableName = tableName;
this.dataId = dataId;
this.type = type;
}
}

View File

@@ -6,8 +6,10 @@ import java.io.Serializable;
/**
* 带业务参数的消息
* @author: jeecg-boot
*/
*
* @author: taoyan
* @date: 2022/8/17
*/
@Data
public class BusMessageDTO extends MessageDTO implements Serializable {

View File

@@ -12,9 +12,8 @@ import java.util.Map;
*/
@Data
public class MessageDTO implements Serializable {
private static final long serialVersionUID = -5690444483968058442L;
/**
* 发送人(用户登录账户)
*/
@@ -45,9 +44,36 @@ public class MessageDTO implements Serializable {
*/
protected String category;
//-----------------------------------------------------------------------
//update-begin---author:taoyan ---date:20220705 for支持自定义推送类型邮件、钉钉、企业微信、系统消息-----------
/**
* 模板消息对应的模板编码
*/
protected String templateCode;
/**
* 消息类型org.jeecg.common.constant.enums.MessageTypeEnum
* XT("system", "系统消息")
* YJ("email", "邮件消息")
* DD("dingtalk", "钉钉消息")
* QYWX("wechat_enterprise", "企业微信")
*/
protected String type;
/**
* 是否发送Markdown格式的消息
*/
protected boolean isMarkdown;
/**
* 解析模板内容 对应的数据
*/
protected Map<String, Object> data;
//update-end---author:taoyan ---date::20220705 for支持自定义推送类型邮件、钉钉、企业微信、系统消息-----------
//-----------------------------------------------------------------------
public MessageDTO(){
}
/**
@@ -73,18 +99,11 @@ public class MessageDTO implements Serializable {
this.category = category;
}
/**
* 模板消息对应的模板编码
*/
protected String templateCode;
/**
* 消息类型org.jeecg.common.constant.enums.MessageTypeEnum
*/
protected String type;
/**
* 解析模板内容 对应的数据
*/
protected Map<String, Object> data;
public boolean isMarkdown() {
return this.isMarkdown;
}
public void setIsMarkdown(boolean isMarkdown) {
this.isMarkdown = isMarkdown;
}
}

View File

@@ -108,7 +108,7 @@ public class DictAspect {
return result;
}
log.info(" __ 进入字典翻译切面 DictAspect —— " );
log.debug(" __ 进入字典翻译切面 DictAspect —— " );
//update-end--Author:zyf -- Date:20220606 ----for【VUEN-1230】 判断是否含有字典注解,没有注解返回-----
for (Object record : records) {
String json="{}";
@@ -274,10 +274,10 @@ public class DictAspect {
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);
log.debug("translateDictFromTableByKeys.dictCode:" + dictCode);
log.debug("translateDictFromTableByKeys.values:" + values);
List<DictModel> texts = commonApi.translateDictFromTableByKeys(table, text, code, values);
log.info("translateDictFromTableByKeys.result:" + texts);
log.debug("translateDictFromTableByKeys.result:" + texts);
List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
list.addAll(texts);
@@ -303,10 +303,10 @@ public class DictAspect {
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);
log.debug("translateManyDict.dictCodes:" + dictCodes);
log.debug("translateManyDict.values:" + values);
Map<String, List<DictModel>> manyDict = commonApi.translateManyDict(dictCodes, values);
log.info("translateManyDict.result:" + manyDict);
log.debug("translateManyDict.result:" + manyDict);
for (String dictCode : manyDict.keySet()) {
List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
List<DictModel> newList = manyDict.get(dictCode);
@@ -374,7 +374,7 @@ public class DictAspect {
}
//update-begin--Author:scott -- Date:20210531 ----for !56 优化微服务应用下存在表字段需要字典翻译时加载缓慢问题-----
if (!StringUtils.isEmpty(table)){
log.info("--DictAspect------dicTable="+ table+" ,dicText= "+text+" ,dicCode="+code);
log.debug("--DictAspect------dicTable="+ table+" ,dicText= "+text+" ,dicCode="+code);
String keyString = String.format("sys:cache:dictTable::SimpleKey [%s,%s,%s,%s]",table,text,code,k.trim());
if (redisTemplate.hasKey(keyString)){
try {

View File

@@ -261,7 +261,7 @@ public interface CommonConstant {
/**
* 在线聊天 图片文件保存路径
*/
String IM_UPLOAD_CUSTOM_PATH = "imfile";
String IM_UPLOAD_CUSTOM_PATH = "biz/user_imgs";
/**
* 在线聊天 用户状态
*/
@@ -406,4 +406,23 @@ public interface CommonConstant {
* 模板消息中 跳转地址的对应的key
*/
String MSG_HREF_URL = "url";
/**
* sys_data_log表的类型 用于区别评论区域的日志数据
*/
String DATA_LOG_TYPE_COMMENT = "comment";
/**
* sys_data_log表的类型 老的数据比较 类型都设置为json
*/
String DATA_LOG_TYPE_JSON = "json";
/** 消息模板markdown */
String MSG_TEMPLATE_TYPE_MD = "5";
/**
* 短信验证码redis-key的前缀
*/
String PHONE_REDIS_KEY_PRE = "phone_msg";
}

View File

@@ -30,6 +30,8 @@ public interface CommonSendStatus {
/**流程催办——系统通知消息模板*/
public static final String TZMB_BPM_CUIBAN = "bpm_cuiban";
/**流程催办——邮件通知消息模板*/
public static final String TZMB_BPM_CUIBAN_EMAIL = "bpm_cuiban_email";
/**标准模板—系统消息通知*/
public static final String TZMB_SYS_TS_NOTE = "sys_ts_note";
/**流程超时提醒——系统通知消息模板*/

View File

@@ -30,7 +30,7 @@ public interface ServiceNameConstants {
*/
String SERVICE_SYSTEM = "jeecg-system";
/**
* 微服务名:Demo模块
* 微服务名: demo模块
*/
String SERVICE_DEMO = "jeecg-demo";

View File

@@ -0,0 +1,22 @@
package org.jeecg.common.constant;
/**
* @Description: TenantConstant
* @author: scott
* @date: 2022年08月29日 15:29
*/
public interface TenantConstant {
/**
* 应用ID——表字段
*/
String DB_FIELD_LOW_APP_ID = "low_app_id";
/**
* 应用ID——实体字段
*/
String FIELD_LOW_APP_ID = "lowAppId";
/**
* 租户ID
*/
String TENANT_ID = "tenantId";
}

View File

@@ -2,8 +2,6 @@ package org.jeecg.common.constant;
/**
* VXESocket 常量
*
* update: 【类名改了大小写】 date: 2022-04-18
* @author: jeecg-boot
*/
public class VxeSocketConst {

View File

@@ -28,6 +28,11 @@ public class WebsocketConst {
*/
public static final String MSG_USER_ID = "userId";
/**
* 消息json key:chat
*/
public static final String MSG_CHAT = "chat";
/**
* 消息类型 heartcheck
*/

View File

@@ -0,0 +1,75 @@
package org.jeecg.common.constant.enums;
import org.jeecg.common.util.oConvertUtils;
/**
* 文件类型
*/
public enum FileTypeEnum {
// 文档类型folder:文件夹 excel:excel doc:word pp:ppt image:图片 archive:其他文档 video:视频)
// FOLDER
xls(".xls","excel","excel"),
xlsx(".xlsx","excel","excel"),
doc(".doc","doc","word"),
docx(".docx","doc","word"),
ppt(".ppt","pp","ppt"),
pptx(".pptx","pp","ppt"),
gif(".gif","image","图片"),
jpg(".jpg","image","图片"),
jpeg(".jpeg","image","图片"),
png(".png","image","图片"),
txt(".txt","text","文本"),
avi(".avi","video","视频"),
mov(".mov","video","视频"),
rmvb(".rmvb","video","视频"),
rm(".rm","video","视频"),
flv(".flv","video","视频"),
mp4(".mp4","video","视频"),
zip(".zip","zip","压缩包"),
pdf(".pdf","pdf","pdf");
private String type;
private String value;
private String text;
private FileTypeEnum(String type,String value,String text){
this.type = type;
this.value = value;
this.text = text;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public static FileTypeEnum getByType(String type){
if (oConvertUtils.isEmpty(type)) {
return null;
}
for (FileTypeEnum val : values()) {
if (val.getType().equals(type)) {
return val;
}
}
return null;
}
}

View File

@@ -13,9 +13,13 @@ import java.util.List;
@EnumDict("messageType")
public enum MessageTypeEnum {
/** 系统消息 */
XT("system", "系统消息"),
/** 邮件消息 */
YJ("email", "邮件消息"),
/** 钉钉消息 */
DD("dingtalk", "钉钉消息"),
/** 企业微信 */
QYWX("wechat_enterprise", "企业微信");
MessageTypeEnum(String type, String note){
@@ -65,4 +69,20 @@ public enum MessageTypeEnum {
}
return list;
}
/**
* 根据type获取枚举
*
* @param type
* @return
*/
public static MessageTypeEnum valueOfType(String type) {
for (MessageTypeEnum e : MessageTypeEnum.values()) {
if (e.getType().equals(type)) {
return e;
}
}
return null;
}
}

View File

@@ -135,7 +135,7 @@ public class SensitiveInfoUtil {
try {
result = AesEncryptUtil.desEncrypt(data);
} catch (Exception exception) {
log.warn("数据解密错误,原数据:"+data);
log.debug("数据解密错误,原数据:"+data);
}
//解决debug模式下加解密失效导致中文被解密变成空的问题
if(oConvertUtils.isEmpty(result) && oConvertUtils.isNotEmpty(data)){

View File

@@ -246,7 +246,7 @@ public class QueryGenerator {
//update-begin-author:taoyan date:2022-5-16 for: issues/3676 获取系统用户列表时使用SQL注入生效
//判断column是不是当前实体的
log.info("当前字段有:"+ allFields);
log.debug("当前字段有:"+ allFields);
if (!allColumnExist(column, allFields)) {
throw new JeecgBootException("请注意,将要排序的列字段不存在:" + column);
}

View File

@@ -0,0 +1,70 @@
package org.jeecg.common.system.vo;
/**
* @Description: 系统文件实体类
* @author: wangshuai
* @date: 2022年08月11日 9:48
*/
public class SysFilesModel {
/**主键id*/
private String id;
/**文件名称*/
private String fileName;
/**文件地址*/
private String url;
/**文档类型folder:文件夹 excel:excel doc:word pp:ppt image:图片 archive:其他文档 video:视频)*/
private String fileType;
/**文件上传类型(temp/本地上传(临时文件) manage/知识库)*/
private String storeType;
/**文件大小kb*/
private Double fileSize;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getFileType() {
return fileType;
}
public void setFileType(String fileType) {
this.fileType = fileType;
}
public String getStoreType() {
return storeType;
}
public void setStoreType(String storeType) {
this.storeType = storeType;
}
public Double getFileSize() {
return fileSize;
}
public void setFileSize(Double fileSize) {
this.fileSize = fileSize;
}
}

View File

@@ -127,10 +127,14 @@ public class CommonUtils {
*/
public static String upload(MultipartFile file, String bizPath, String uploadType) {
String url = "";
if(CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)){
url = MinioUtil.upload(file,bizPath);
}else{
url = OssBootUtil.upload(file,bizPath);
try {
if (CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)) {
url = MinioUtil.upload(file, bizPath);
} else {
url = OssBootUtil.upload(file, bizPath);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return url;
}
@@ -186,10 +190,14 @@ public class CommonUtils {
*/
public static String upload(MultipartFile file, String bizPath, String uploadType, String customBucket) {
String url = "";
if(CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)){
url = MinioUtil.upload(file,bizPath,customBucket);
}else{
url = OssBootUtil.upload(file,bizPath,customBucket);
try {
if (CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)) {
url = MinioUtil.upload(file, bizPath, customBucket);
} else {
url = OssBootUtil.upload(file, bizPath, customBucket);
}
} catch (Exception e) {
log.error(e.getMessage(),e);
}
return url;
}
@@ -340,7 +348,7 @@ public class CommonUtils {
}else{
baseDomainPath = scheme + "://" + serverName + ":" + serverPort + contextPath ;
}
log.info("-----Common getBaseUrl----- : " + baseDomainPath);
log.debug("-----Common getBaseUrl----- : " + baseDomainPath);
return baseDomainPath;
}
}

View File

@@ -671,4 +671,17 @@ public class DateUtils extends PropertyEditorSupport {
return calendar.get(Calendar.YEAR);
}
/**
* 将字符串转成时间
* @param str
* @return
*/
public static Date parseDatetime(String str){
try {
return datetimeFormat.get().parse(str);
}catch (Exception e){
}
return null;
}
}

View File

@@ -1,6 +1,7 @@
package org.jeecg.common.util;
import org.apache.commons.lang3.StringUtils;
import org.pegdown.PegDownProcessor;
import org.springframework.web.util.HtmlUtils;
/**
@@ -29,4 +30,14 @@ public class HTMLUtils {
return "";
}
/**
* 将Markdown解析成Html
* @param markdownContent
* @return
*/
public static String parseMarkdown(String markdownContent) {
PegDownProcessor pdp = new PegDownProcessor();
return pdp.markdownToHtml(markdownContent);
}
}

View File

@@ -11,8 +11,6 @@ import org.slf4j.LoggerFactory;
* IP地址
*
* @Author scott
*
* update: 【类名改了大小写】 date: 2022-04-18
* @email jeecgos@163.com
* @Date 2019年01月14日
*/

View File

@@ -4,8 +4,6 @@ import java.security.MessageDigest;
/**
* @Description: 加密工具
*
* update: 【类名改了大小写】 date: 2022-04-18
* @author: jeecg-boot
*/
public class Md5Util {

View File

@@ -53,11 +53,16 @@ public class MinioUtil {
* @param file
* @return
*/
public static String upload(MultipartFile file, String bizPath, String customBucket) {
public static String upload(MultipartFile file, String bizPath, String customBucket) throws Exception {
String fileUrl = "";
//update-begin-author:wangshuai date:20201012 for: 过滤上传文件夹名特殊字符,防止攻击
bizPath=StrAttackFilter.filter(bizPath);
bizPath = StrAttackFilter.filter(bizPath);
//update-end-author:wangshuai date:20201012 for: 过滤上传文件夹名特殊字符,防止攻击
//update-begin-author:liusq date:20210809 for: 过滤上传文件类型
FileTypeFilter.fileTypeFilter(file);
//update-end-author:liusq date:20210809 for: 过滤上传文件类型
String newBucket = bucketName;
if(oConvertUtils.isNotEmpty(customBucket)){
newBucket = customBucket;
@@ -72,9 +77,6 @@ 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();
@@ -111,8 +113,8 @@ public class MinioUtil {
* @param bizPath
* @return
*/
public static String upload(MultipartFile file, String bizPath) {
return upload(file,bizPath,null);
public static String upload(MultipartFile file, String bizPath) throws Exception {
return upload(file,bizPath,null);
}
/**

View File

@@ -3,6 +3,7 @@ package org.jeecg.common.util;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.config.JeecgBaseConfig;
import org.springframework.http.*;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
@@ -46,7 +47,18 @@ public class RestUtil {
}
public static String getBaseUrl() {
String basepath = getDomain() + getPath();
String basepath = null;
try {
basepath = getDomain() + getPath();
} catch (Exception e) {
log.warn(e.getMessage(),e);
}
//定时任务情况下通过request是获取不到domain的这种情况下通过配置获取pc后台域名
if(oConvertUtils.isEmpty(basepath)){
JeecgBaseConfig jeecgBaseConfig = SpringContextUtils.getBean(JeecgBaseConfig.class);
basepath = jeecgBaseConfig.getDomainUrl().getPc();
}
log.info(" RestUtil.getBaseUrl: " + basepath);
return basepath;
}

View File

@@ -10,7 +10,7 @@ public enum SysAnnmentTypeEnum {
*/
EMAIL("email", "component", "modules/eoa/email/modals/EoaEmailInForm"),
/**
* 工作流跳转链接我的办公
* 流程跳转到我的任务
*/
BPM("bpm", "url", "/bpm/task/MyTaskList");

View File

@@ -2,6 +2,7 @@ package org.jeecg.common.util.dynamic.db;
import freemarker.cache.StringTemplateLoader;
import freemarker.core.ParseException;
import freemarker.core.TemplateClassResolver;
import freemarker.template.Configuration;
import freemarker.template.Template;
import lombok.extern.slf4j.Slf4j;
@@ -53,6 +54,11 @@ public class FreemarkerParseFactory {
SQL_CONFIG.setNumberFormat("0.#####################");
//classic_compatible设置解决报空指针错误
SQL_CONFIG.setClassicCompatible(true);
//update-begin-author:taoyan date:2022-8-10 for: freemarker模板注入问题 禁止解析ObjectConstructorExecute和freemarker.template.utility.JythonRuntime。
//https://ackcent.com/in-depth-freemarker-template-injection/
SQL_CONFIG.setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER);
//update-end-author:taoyan date:2022-8-10 for: freemarker模板注入问题 禁止解析ObjectConstructorExecute和freemarker.template.utility.JythonRuntime。
}
/**
@@ -115,8 +121,10 @@ public class FreemarkerParseFactory {
* @param paras 参数
* @return String 模板解析后内容
*/
public static String parseTemplateContent(String tplContent,
Map<String, Object> paras) {
public static String parseTemplateContent(String tplContent,Map<String, Object> paras) {
return parseTemplateContent(tplContent, paras, false);
}
public static String parseTemplateContent(String tplContent, Map<String, Object> paras, boolean keepSpace) {
try {
String sqlUnderline="sql_";
StringWriter swriter = new StringWriter();
@@ -129,7 +137,7 @@ public class FreemarkerParseFactory {
}
paras.put(MINI_DAO_FORMAT, new SimpleFormat());
mytpl.process(paras, swriter);
String sql = getSqlText(swriter.toString());
String sql = getSqlText(swriter.toString(), keepSpace);
paras.remove(MINI_DAO_FORMAT);
return sql;
} catch (Exception e) {
@@ -145,10 +153,16 @@ public class FreemarkerParseFactory {
* 除去无效字段,去掉注释 不然批量处理可能报错 去除无效的等于
*/
private static String getSqlText(String sql) {
return getSqlText(sql, false);
}
private static String getSqlText(String sql, boolean keepSpace) {
// 将注释替换成""
sql = NOTES_PATTERN.matcher(sql).replaceAll("");
sql = sql.replaceAll("\\n", " ").replaceAll("\\t", " ")
.replaceAll("\\s{1,}", " ").trim();
if (!keepSpace) {
sql = sql.replaceAll("\\n", " ").replaceAll("\\t", " ")
.replaceAll("\\s{1,}", " ").trim();
}
// 去掉 最后是 where这样的问题
//where空格 "where "
String whereSpace = DataBaseConstant.SQL_WHERE+" ";

View File

@@ -98,7 +98,7 @@ public class FileTypeFilter {
String suffix = getFileType(file);
for (String type : forbidType) {
if (type.contains(suffix)) {
throw new Exception("上传失败,文件类型异常" + suffix);
throw new Exception("上传失败,非法文件类型:" + suffix);
}
}
}

View File

@@ -96,7 +96,11 @@ public class OssBootUtil {
* @param fileDir 文件保存目录
* @return oss 中的相对文件路径
*/
public static String upload(MultipartFile file, String fileDir,String customBucket) {
public static String upload(MultipartFile file, String fileDir,String customBucket) throws Exception {
//update-begin-author:liusq date:20210809 for: 过滤上传文件类型
FileTypeFilter.fileTypeFilter(file);
//update-end-author:liusq date:20210809 for: 过滤上传文件类型
String filePath = null;
initOss(endPoint, accessKeyId, accessKeySecret);
StringBuilder fileUrl = new StringBuilder();
@@ -114,9 +118,6 @@ 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()
@@ -169,7 +170,7 @@ public class OssBootUtil {
* @param fileDir
* @return
*/
public static String upload(MultipartFile file, String fileDir) {
public static String upload(MultipartFile file, String fileDir) throws Exception {
return upload(file, fileDir,null);
}
@@ -235,6 +236,8 @@ public class OssBootUtil {
} else {
bucketUrl = "https://" + newBucket + "." + endPoint + SymbolConstant.SINGLE_SLASH;
}
//TODO 暂时不允许删除云存储的文件
//initOss(endPoint, accessKeyId, accessKeySecret);
url = url.replace(bucketUrl,"");
ossClient.deleteObject(newBucket, url);
}

View File

@@ -0,0 +1,46 @@
package org.jeecg.common.util.security;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.util.oConvertUtils;
/**
* jdbc连接校验
* @Author taoYan
* @Date 2022/8/10 18:15
**/
public class JdbcSecurityUtil {
/**
* 连接驱动漏洞 最新版本修复后可删除相应的key
* postgreauthenticationPluginClassName, sslhostnameverifier, socketFactory, sslfactory, sslpasswordcallback
* https://github.com/pgjdbc/pgjdbc/security/advisories/GHSA-v7wg-cpwc-24m4
*
*/
public static final String[] notAllowedProps = new String[]{"authenticationPluginClassName", "sslhostnameverifier", "socketFactory", "sslfactory", "sslpasswordcallback"};
/**
* 校验sql是否有特定的key
* @param jdbcUrl
* @return
*/
public static void validate(String jdbcUrl){
if(oConvertUtils.isEmpty(jdbcUrl)){
return;
}
String urlConcatChar = "?";
if(jdbcUrl.indexOf(urlConcatChar)<0){
return;
}
String argString = jdbcUrl.substring(jdbcUrl.indexOf(urlConcatChar)+1);
String[] keyAndValues = argString.split("&");
for(String temp: keyAndValues){
String key = temp.split("=")[0];
for(String prop: notAllowedProps){
if(prop.equalsIgnoreCase(key)){
throw new JeecgBootException("连接地址有安全风险,【"+key+"");
}
}
}
}
}

View File

@@ -23,7 +23,11 @@ public class JeecgBaseConfig {
* 需要加强校验的接口清单
*/
private String signUrls;
/**
* 上传模式
* 本地local\Miniominio\阿里云alioss
*/
private String uploadType;
/**
* 是否启用安全模式
*/
@@ -44,6 +48,11 @@ public class JeecgBaseConfig {
*/
private DomainUrl domainUrl;
/**
* 文件预览
*/
private String fileViewDomain;
public Boolean getSafeMode() {
return safeMode;
}
@@ -83,7 +92,6 @@ public class JeecgBaseConfig {
public void setDomainUrl(DomainUrl domainUrl) {
this.domainUrl = domainUrl;
}
public String getSignUrls() {
return signUrls;
}
@@ -91,4 +99,13 @@ public class JeecgBaseConfig {
public void setSignUrls(String signUrls) {
this.signUrls = signUrls;
}
public String getFileViewDomain() {
return fileViewDomain;
}
public void setFileViewDomain(String fileViewDomain) {
this.fileViewDomain = fileViewDomain;
}
}

View File

@@ -75,8 +75,8 @@ public class Swagger2Config implements WebMvcConfigurer {
.paths(PathSelectors.any())
.build()
.securitySchemes(Collections.singletonList(securityScheme()))
.securityContexts(securityContexts());
//.globalOperationParameters(setHeaderToken());
.securityContexts(securityContexts())
.globalOperationParameters(setHeaderToken());
}
/***
@@ -109,7 +109,7 @@ public class Swagger2Config implements WebMvcConfigurer {
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
// //大标题
.title("Jeecg-Boot 后台服务API接口文档")
.title("JeecgBoot 后台服务API接口文档")
// 版本号
.version("1.0")
// .termsOfServiceUrl("NO terms of service")
@@ -117,7 +117,6 @@ public class Swagger2Config implements WebMvcConfigurer {
.description("后台API接口")
// 作者
.contact(new Contact("北京国炬信息技术有限公司","www.jeccg.com","jeecgos@163.com"))
// .contact("JEECG团队")
.license("The Apache License, Version 2.0")
.licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
.build();

View File

@@ -29,6 +29,7 @@ import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
@@ -45,7 +46,7 @@ import java.util.List;
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Autowired
@Resource
JeecgBaseConfig jeecgBaseConfig;
@Value("${spring.resource.static-locations:}")
private String staticLocations;

View File

@@ -30,7 +30,8 @@ public class WebSocketConfig {
public FilterRegistrationBean getFilterRegistrationBean(){
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(websocketFilter());
bean.addUrlPatterns("/websocket/*", "/eoaSocket/*", "/newsWebsocket/*", "/vxeSocket/*");
//TODO 临时注释掉测试下线上socket总断的问题
bean.addUrlPatterns("/websocket/*","/eoaSocket/*", "/newsWebsocket/*", "/vxeSocket/*");
return bean;
}

View File

@@ -5,6 +5,7 @@ import org.jeecg.common.api.CommonAPI;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.TokenUtils;
import org.jeecg.common.util.oConvertUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
@@ -36,12 +37,13 @@ public class WebsocketFilter implements Filter {
HttpServletRequest request = (HttpServletRequest)servletRequest;
String token = request.getHeader(TOKEN_KEY);
log.info("websocket连接 Token安全校验Path = {}token:{}", request.getRequestURI(), token);
log.debug("Websocket连接 Token安全校验Path = {}token:{}", request.getRequestURI(), token);
try {
TokenUtils.verifyToken(token, commonApi, redisUtil);
} catch (Exception exception) {
log.error("websocket连接校验失败{}token:{}", exception.getMessage(), token);
//log.error("Websocket连接 Token安全校验失败IP:{}, Token:{}, Path = {},异常:{}", oConvertUtils.getIpAddrByRequest(request), token, request.getRequestURI(), exception.getMessage());
log.debug("Websocket连接 Token安全校验失败IP:{}, Token:{}, Path = {},异常:{}", oConvertUtils.getIpAddrByRequest(request), token, request.getRequestURI(), exception.getMessage());
return;
}
HttpServletResponse response = (HttpServletResponse)servletResponse;

View File

@@ -66,7 +66,7 @@ public class ShiroConfig {
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
//支持yml方式配置拦截排除
if(jeecgBaseConfig.getShiro()!=null){
if(jeecgBaseConfig!=null && jeecgBaseConfig.getShiro()!=null){
String shiroExcludeUrls = jeecgBaseConfig.getShiro().getExcludeUrls();
if(oConvertUtils.isNotEmpty(shiroExcludeUrls)){
String[] permissionUrl = shiroExcludeUrls.split(",");
@@ -109,6 +109,7 @@ public class ShiroConfig {
filterChainDefinitionMap.put("/**/*.pdf", "anon");
filterChainDefinitionMap.put("/**/*.jpg", "anon");
filterChainDefinitionMap.put("/**/*.png", "anon");
filterChainDefinitionMap.put("/**/*.gif", "anon");
filterChainDefinitionMap.put("/**/*.ico", "anon");
// update-begin--Author:sunjianlei Date:20190813 for排除字体格式的后缀

View File

@@ -2,7 +2,6 @@ package org.jeecg.config.sign.interceptor;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.util.PathMatcherUtil;
import org.jeecg.common.util.SpringContextHolder;
import org.jeecg.config.JeecgBaseConfig;
import org.jeecg.config.filter.RequestBodyReserveFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
@@ -12,8 +11,6 @@ import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
/**
* 签名 拦截器配置

View File

@@ -10,6 +10,7 @@ import org.jeecg.config.JeecgBaseConfig;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import java.io.UnsupportedEncodingException;
import java.util.SortedMap;
/**
@@ -54,6 +55,12 @@ public class SignUtil {
if(oConvertUtils.isEmpty(signatureSecret) || signatureSecret.contains(curlyBracket)){
throw new JeecgBootException("签名密钥 ${jeecg.signatureSecret} 缺少配置 ");
}
return DigestUtils.md5DigestAsHex((paramsJsonStr + signatureSecret).getBytes()).toUpperCase();
try {
//【issues/I484RW】2.4.6部署后下拉搜索框提示“sign签名检验失败”
return DigestUtils.md5DigestAsHex((paramsJsonStr + signatureSecret).getBytes("UTF-8")).toUpperCase();
} catch (UnsupportedEncodingException e) {
log.error(e.getMessage(),e);
return null;
}
}
}