mirror of
https://github.com/jeecgboot/JeecgBoot.git
synced 2025-12-08 17:12:28 +08:00
Compare commits
19 Commits
199d2b439e
...
springboot
| Author | SHA1 | Date | |
|---|---|---|---|
| 74d80459e1 | |||
| bdc1e7988b | |||
| 5730ed2dd2 | |||
| 5743d71f3c | |||
| db16cb5fda | |||
| 07c012adc7 | |||
| 8edb547113 | |||
| da9e8570cd | |||
| 5e37b4de8f | |||
| cab42b819c | |||
| d3b8948f40 | |||
| 766ec0df52 | |||
| b72c343090 | |||
| cdd7c5c5a1 | |||
| 120cf85eb4 | |||
| a3969a5c63 | |||
| 1222c76ff8 | |||
| 6048866313 | |||
| 83edfa9090 |
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -9,6 +9,8 @@ assignees: getActivity
|
||||
|
||||
##### 版本号:
|
||||
|
||||
##### 分支:
|
||||
|
||||
|
||||
##### 问题描述:
|
||||
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/feature_request.md
vendored
3
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -6,9 +6,10 @@ assignees: getActivity
|
||||
|
||||
---
|
||||
|
||||
|
||||
##### 版本号:
|
||||
|
||||
##### 分支:
|
||||
|
||||
|
||||
##### 问题描述:
|
||||
|
||||
|
||||
@ -7,12 +7,12 @@
|
||||
JEECG BOOT AI Low Code Platform
|
||||
===============
|
||||
|
||||
Current version: 3.8.2 (Release date: 2025-08-04)
|
||||
Current version: 3.8.3 (Release date: 2025-10-09)
|
||||
|
||||
|
||||
[](https://github.com/zhangdaiscott/jeecg-boot/blob/master/LICENSE)
|
||||
[](http://www.jeecg.com)
|
||||
[](https://github.com/zhangdaiscott/jeecg-boot)
|
||||
[](https://github.com/zhangdaiscott/jeecg-boot)
|
||||
[](https://github.com/zhangdaiscott/jeecg-boot)
|
||||
[](https://github.com/zhangdaiscott/jeecg-boot)
|
||||
|
||||
|
||||
@ -2,8 +2,9 @@
|
||||
JeecgBoot AI低代码平台
|
||||
===============
|
||||
|
||||
当前最新版本: 3.8.3(发布日期:2025-09-22)
|
||||
当前最新版本: 3.8.3(发布日期:2025-10-09)
|
||||
|
||||
> 重要提醒: v3.8.3 是 SpringBoot2 的最终版本,后续将不再维护。建议大家尽快升级至基于 SpringBoot3版本,以获得更好的性能和支持。
|
||||
|
||||
[](https://github.com/jeecgboot/JeecgBoot/blob/master/LICENSE)
|
||||
[](https://jeecg.com)
|
||||
@ -81,6 +82,7 @@ JeecgBoot低代码平台,可以应用在任何J2EE项目的开发中,支持
|
||||
- 官方网站: [http://www.jeecg.com](http://www.jeecg.com)
|
||||
- 在线演示: [平台演示](https://boot3.jeecg.com) | [APP演示](https://jeecg.com/appIndex)
|
||||
- 入门指南: [快速入门](http://www.jeecg.com/doc/quickstart) | [代码生成使用](https://help.jeecg.com/java/codegen/online) | [开发文档](https://help.jeecg.com) | [AI应用手册](https://help.jeecg.com/aigc) | [视频教程](http://jeecg.com/doc/video)
|
||||
- AI编程: [JEECG低代码+AI编程Cursor+GitHub Copilot实现高效编程](https://www.bilibili.com/video/BV11XyaBVEoH)
|
||||
- 技术支持: [反馈问题](https://github.com/jeecgboot/JeecgBoot/issues/new?template=bug_report.md) | [低代码体验一分钟](https://jeecg.blog.csdn.net/article/details/106079007)
|
||||
- QQ交流群 : 964611995、⑩716488839(满)、⑨808791225(满)、其他(满)
|
||||
|
||||
@ -155,6 +157,9 @@ JeecgBoot平台提供了一套完善的AI应用管理系统模块,是一套类
|
||||
#### 前端
|
||||
|
||||
- 前端环境要求:Node.js要求`Node 20+` 版本以上、pnpm 要求`9+` 版本以上
|
||||
|
||||
> Vite 不再支持已结束生命周期(EOL)的 Node.js 18。现在需要使用 Node.js 20.19+ 或 22.12+。
|
||||
|
||||
- 依赖管理:node、npm、pnpm
|
||||
- 前端IDE建议:IDEA、WebStorm、Vscode
|
||||
- 采用 Vue3.0+TypeScript+Vite6+Ant-Design-Vue4等新技术方案,包括二次封装组件、utils、hooks、动态菜单、权限校验、按钮级别权限控制等功能
|
||||
@ -508,4 +513,4 @@ AI写文章
|
||||
|
||||
如果觉得还不错,请作者喝杯咖啡吧 ☺
|
||||
|
||||

|
||||

|
||||
|
||||
@ -109,28 +109,33 @@ services:
|
||||
# environment:
|
||||
# RABBITMQ_DEFAULT_USER: guest
|
||||
# RABBITMQ_DEFAULT_PASS: guest
|
||||
# jeecg-boot-sentinel:
|
||||
# restart: on-failure
|
||||
# build:
|
||||
# context: ./jeecg-visual/jeecg-cloud-sentinel
|
||||
# ports:
|
||||
# - 9000:9000
|
||||
# depends_on:
|
||||
# - jeecg-boot-nacos
|
||||
# - jeecg-boot-demo
|
||||
# - jeecg-boot-system
|
||||
# - jeecg-boot-gateway
|
||||
# container_name: jeecg-boot-sentinel
|
||||
# hostname: jeecg-boot-sentinel
|
||||
#
|
||||
# jeecg-boot-xxljob:
|
||||
# build:
|
||||
# context: ./jeecg-visual/jeecg-cloud-xxljob
|
||||
# ports:
|
||||
# - 9080:9080
|
||||
# container_name: jeecg-boot-xxljob
|
||||
# hostname: jeecg-boot-xxljob
|
||||
|
||||
jeecg-boot-sentinel:
|
||||
restart: on-failure
|
||||
build:
|
||||
context: ./jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-sentinel
|
||||
ports:
|
||||
- 9000:9000
|
||||
depends_on:
|
||||
- jeecg-boot-nacos
|
||||
- jeecg-boot-demo
|
||||
- jeecg-boot-system
|
||||
- jeecg-boot-gateway
|
||||
container_name: jeecg-boot-sentinel
|
||||
hostname: jeecg-boot-sentinel
|
||||
networks:
|
||||
- jeecg-boot
|
||||
|
||||
jeecg-boot-xxljob:
|
||||
build:
|
||||
context: ./jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-xxljob
|
||||
ports:
|
||||
- 9080:9080
|
||||
container_name: jeecg-boot-xxljob
|
||||
hostname: jeecg-boot-xxljob
|
||||
networks:
|
||||
- jeecg-boot
|
||||
|
||||
jeecg-vue:
|
||||
build:
|
||||
context: ./jeecgboot-vue3
|
||||
|
||||
File diff suppressed because one or more lines are too long
BIN
jeecg-boot/db/其他数据库脚本/jeecgboot-oracle11g.dmp
Normal file
BIN
jeecg-boot/db/其他数据库脚本/jeecgboot-oracle11g.dmp
Normal file
Binary file not shown.
29702
jeecg-boot/db/其他数据库脚本/jeecgboot-oracle11g.sql
Normal file
29702
jeecg-boot/db/其他数据库脚本/jeecgboot-oracle11g.sql
Normal file
File diff suppressed because one or more lines are too long
25749
jeecg-boot/db/其他数据库脚本/jeecgboot-postgresql17.sql
Normal file
25749
jeecg-boot/db/其他数据库脚本/jeecgboot-postgresql17.sql
Normal file
File diff suppressed because one or more lines are too long
48117
jeecg-boot/db/其他数据库脚本/jeecgboot-sqlserver2017.sql
Normal file
48117
jeecg-boot/db/其他数据库脚本/jeecgboot-sqlserver2017.sql
Normal file
File diff suppressed because one or more lines are too long
@ -143,7 +143,7 @@ public class AutoLogAspect {
|
||||
// https://my.oschina.net/mengzhang6/blog/2395893
|
||||
Object[] arguments = new Object[paramsArray.length];
|
||||
for (int i = 0; i < paramsArray.length; i++) {
|
||||
if (paramsArray[i] instanceof BindingResult || paramsArray[i] instanceof ServletRequest || paramsArray[i] instanceof ServletResponse || paramsArray[i] instanceof MultipartFile) {
|
||||
if (paramsArray[i] instanceof BindingResult || paramsArray[i] instanceof ServletRequest || paramsArray[i] instanceof ServletResponse || paramsArray[i] instanceof MultipartFile || paramsArray[i] instanceof MultipartFile[]) {
|
||||
//ServletRequest不能序列化,从入参里排除,否则报异常:java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
|
||||
//ServletResponse不能序列化 从入参里排除,否则报异常:java.lang.IllegalStateException: getOutputStream() has already been called for this response
|
||||
continue;
|
||||
|
||||
@ -153,9 +153,9 @@ public class CommonUtils {
|
||||
*/
|
||||
public static String uploadLocal(MultipartFile mf,String bizPath,String uploadpath){
|
||||
try {
|
||||
//update-begin-author:liusq date:20210809 for: 过滤上传文件类型
|
||||
SsrfFileTypeFilter.checkUploadFileType(mf);
|
||||
//update-end-author:liusq date:20210809 for: 过滤上传文件类型
|
||||
// 文件安全校验,防止上传漏洞文件
|
||||
SsrfFileTypeFilter.checkUploadFileType(mf, bizPath);
|
||||
|
||||
String fileName = null;
|
||||
File file = new File(uploadpath + File.separator + bizPath + File.separator );
|
||||
if (!file.exists()) {
|
||||
|
||||
@ -55,13 +55,11 @@ public class MinioUtil {
|
||||
*/
|
||||
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);
|
||||
//update-end-author:wangshuai date:20201012 for: 过滤上传文件夹名特殊字符,防止攻击
|
||||
|
||||
//update-begin-author:liusq date:20210809 for: 过滤上传文件类型
|
||||
SsrfFileTypeFilter.checkUploadFileType(file);
|
||||
//update-end-author:liusq date:20210809 for: 过滤上传文件类型
|
||||
// 文件安全校验,防止上传漏洞文件
|
||||
SsrfFileTypeFilter.checkUploadFileType(file, bizPath);
|
||||
|
||||
String newBucket = bucketName;
|
||||
if(oConvertUtils.isNotEmpty(customBucket)){
|
||||
|
||||
@ -2,6 +2,7 @@ package org.jeecg.common.util.filter;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jeecg.common.exception.JeecgBootException;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -149,29 +150,38 @@ public class SsrfFileTypeFilter {
|
||||
public static void checkDownloadFileType(String filePath) throws IOException {
|
||||
//文件后缀
|
||||
String suffix = getFileTypeBySuffix(filePath);
|
||||
log.info("suffix:{}", suffix);
|
||||
log.debug(" 【文件下载校验】文件后缀 suffix: {}", suffix);
|
||||
boolean isAllowExtension = FILE_TYPE_WHITE_LIST.contains(suffix.toLowerCase());
|
||||
//是否允许下载的文件
|
||||
if (!isAllowExtension) {
|
||||
throw new IOException("下载失败,存在非法文件类型:" + suffix);
|
||||
throw new JeecgBootException("下载失败,存在非法文件类型:" + suffix);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 上传文件类型过滤
|
||||
*
|
||||
* @param file
|
||||
*/
|
||||
public static void checkUploadFileType(MultipartFile file) throws Exception {
|
||||
//获取文件真是后缀
|
||||
String suffix = getFileType(file);
|
||||
|
||||
log.info("suffix:{}", suffix);
|
||||
checkUploadFileType(file, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件类型过滤
|
||||
*
|
||||
* @param file
|
||||
*/
|
||||
public static void checkUploadFileType(MultipartFile file, String customPath) throws Exception {
|
||||
//1. 路径安全校验
|
||||
validatePathSecurity(customPath);
|
||||
//2. 校验文件后缀和头
|
||||
String suffix = getFileType(file, customPath);
|
||||
log.info("【文件上传校验】文件后缀 suffix: {},customPath:{}", suffix, customPath);
|
||||
boolean isAllowExtension = FILE_TYPE_WHITE_LIST.contains(suffix.toLowerCase());
|
||||
//是否允许下载的文件
|
||||
if (!isAllowExtension) {
|
||||
throw new Exception("上传失败,存在非法文件类型:" + suffix);
|
||||
throw new JeecgBootException("上传失败,存在非法文件类型:" + suffix);
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,7 +193,7 @@ public class SsrfFileTypeFilter {
|
||||
* @throws Exception
|
||||
*/
|
||||
|
||||
private static String getFileType(MultipartFile file) throws Exception {
|
||||
private static String getFileType(MultipartFile file, String customPath) throws Exception {
|
||||
//update-begin-author:liusq date:20230404 for: [issue/4672]方法造成的文件被占用,注释掉此方法tomcat就能自动清理掉临时文件
|
||||
String fileExtendName = null;
|
||||
InputStream is = null;
|
||||
@ -203,7 +213,7 @@ public class SsrfFileTypeFilter {
|
||||
break;
|
||||
}
|
||||
}
|
||||
log.info("-----获取到的指定文件类型------"+fileExtendName);
|
||||
log.debug("-----获取到的指定文件类型------"+fileExtendName);
|
||||
// 如果不是上述类型,则判断扩展名
|
||||
if (StringUtils.isBlank(fileExtendName)) {
|
||||
String fileName = file.getOriginalFilename();
|
||||
@ -214,7 +224,6 @@ public class SsrfFileTypeFilter {
|
||||
// 如果有扩展名,则返回扩展名
|
||||
return getFileTypeBySuffix(fileName);
|
||||
}
|
||||
log.info("-----最終的文件类型------"+fileExtendName);
|
||||
is.close();
|
||||
return fileExtendName;
|
||||
} catch (Exception e) {
|
||||
@ -249,4 +258,34 @@ public class SsrfFileTypeFilter {
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 路径安全校验
|
||||
*/
|
||||
private static void validatePathSecurity(String customPath) throws JeecgBootException {
|
||||
if (customPath == null || customPath.trim().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 统一分隔符为 /
|
||||
String normalized = customPath.replace("\\", "/");
|
||||
|
||||
// 1. 防止路径遍历攻击
|
||||
if (normalized.contains("..") || normalized.contains("~")) {
|
||||
throw new JeecgBootException("上传业务路径包含非法字符!");
|
||||
}
|
||||
|
||||
// 2. 限制路径深度
|
||||
int depth = normalized.split("/").length;
|
||||
if (depth > 5) {
|
||||
throw new JeecgBootException("上传业务路径深度超出限制!");
|
||||
}
|
||||
|
||||
// 3. 限制字符集(只允许字母、数字、下划线、横线、斜杠)
|
||||
if (!normalized.matches("^[a-zA-Z0-9/_-]+$")) {
|
||||
throw new JeecgBootException("上传业务路径包含非法字符!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -97,9 +97,8 @@ public class OssBootUtil {
|
||||
* @return oss 中的相对文件路径
|
||||
*/
|
||||
public static String upload(MultipartFile file, String fileDir,String customBucket) throws Exception {
|
||||
//update-begin-author:liusq date:20210809 for: 过滤上传文件类型
|
||||
// 文件安全校验,防止上传漏洞文件
|
||||
SsrfFileTypeFilter.checkUploadFileType(file);
|
||||
//update-end-author:liusq date:20210809 for: 过滤上传文件类型
|
||||
|
||||
String filePath = null;
|
||||
initOss(endPoint, accessKeyId, accessKeySecret);
|
||||
|
||||
@ -158,7 +158,7 @@ public class ShiroConfig {
|
||||
filterChainDefinitionMap.put("/drag/onlDragDatasetHead/getTotalDataByCompId", "anon");
|
||||
filterChainDefinitionMap.put("/drag/mock/json/**", "anon");
|
||||
filterChainDefinitionMap.put("/drag/onlDragDatasetHead/getDictByCodes", "anon");
|
||||
|
||||
filterChainDefinitionMap.put("/drag/onlDragDatasetHead/queryAllById", "anon");
|
||||
filterChainDefinitionMap.put("/jimubi/view", "anon");
|
||||
filterChainDefinitionMap.put("/jimubi/share/view/**", "anon");
|
||||
|
||||
|
||||
@ -68,50 +68,19 @@ public class CommonController {
|
||||
Result<?> result = new Result<>();
|
||||
String savePath = "";
|
||||
String bizPath = request.getParameter("biz");
|
||||
|
||||
//LOWCOD-2580 sys/common/upload接口存在任意文件上传漏洞
|
||||
if (oConvertUtils.isNotEmpty(bizPath)) {
|
||||
if(bizPath.contains(SymbolConstant.SPOT_SINGLE_SLASH) || bizPath.contains(SymbolConstant.SPOT_DOUBLE_BACKSLASH)){
|
||||
throw new JeecgBootException("上传目录bizPath,格式非法!");
|
||||
}
|
||||
}
|
||||
|
||||
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
|
||||
// 获取上传文件对象
|
||||
MultipartFile file = multipartRequest.getFile("file");
|
||||
if(oConvertUtils.isEmpty(bizPath)){
|
||||
if(CommonConstant.UPLOAD_TYPE_OSS.equals(uploadType)){
|
||||
//未指定目录,则用阿里云默认目录 upload
|
||||
bizPath = "upload";
|
||||
//result.setMessage("使用阿里云文件上传时,必须添加目录!");
|
||||
//result.setSuccess(false);
|
||||
//return result;
|
||||
}else{
|
||||
bizPath = "";
|
||||
}
|
||||
|
||||
// 文件安全校验,防止上传漏洞文件
|
||||
SsrfFileTypeFilter.checkUploadFileType(file, bizPath);
|
||||
|
||||
if (oConvertUtils.isEmpty(bizPath)) {
|
||||
bizPath = CommonConstant.UPLOAD_TYPE_OSS.equals(uploadType) ? "upload" : "";
|
||||
}
|
||||
if(CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)){
|
||||
//update-begin-author:liusq date:20221102 for: 过滤上传文件类型
|
||||
SsrfFileTypeFilter.checkUploadFileType(file);
|
||||
//update-end-author:liusq date:20221102 for: 过滤上传文件类型
|
||||
//update-begin-author:lvdandan date:20200928 for:修改JEditor编辑器本地上传
|
||||
savePath = this.uploadLocal(file,bizPath);
|
||||
//update-begin-author:lvdandan date:20200928 for:修改JEditor编辑器本地上传
|
||||
/** 富文本编辑器及markdown本地上传时,采用返回链接方式
|
||||
//针对jeditor编辑器如何使 lcaol模式,采用 base64格式存储
|
||||
String jeditor = request.getParameter("jeditor");
|
||||
if(oConvertUtils.isNotEmpty(jeditor)){
|
||||
result.setMessage(CommonConstant.UPLOAD_TYPE_LOCAL);
|
||||
result.setSuccess(true);
|
||||
return result;
|
||||
}else{
|
||||
savePath = this.uploadLocal(file,bizPath);
|
||||
}
|
||||
*/
|
||||
}else{
|
||||
//update-begin-author:taoyan date:20200814 for:文件上传改造
|
||||
savePath = CommonUtils.upload(file, bizPath, uploadType);
|
||||
//update-end-author:taoyan date:20200814 for:文件上传改造
|
||||
}
|
||||
if(oConvertUtils.isNotEmpty(savePath)){
|
||||
result.setMessage(savePath);
|
||||
|
||||
@ -133,7 +133,7 @@ public class SysTenantController {
|
||||
@RequestMapping(value = "/add", method = RequestMethod.POST)
|
||||
public Result<SysTenant> add(@RequestBody SysTenant sysTenant) {
|
||||
Result<SysTenant> result = new Result();
|
||||
if(sysTenantService.getById(sysTenant.getId())!=null){
|
||||
if(sysTenant!=null && oConvertUtils.isNotEmpty(sysTenant.getId()) && sysTenantService.getById(sysTenant.getId())!=null){
|
||||
return result.error500("该编号已存在!");
|
||||
}
|
||||
try {
|
||||
|
||||
@ -2,9 +2,9 @@ package org.jeecg.modules.system.controller;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.exception.JeecgBootException;
|
||||
import org.jeecg.common.util.CommonUtils;
|
||||
import org.jeecg.common.util.MinioUtil;
|
||||
import org.jeecg.common.util.filter.SsrfFileTypeFilter;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.oss.entity.OssFile;
|
||||
import org.jeecg.modules.oss.service.IOssFileService;
|
||||
@ -35,20 +35,18 @@ public class SysUploadController {
|
||||
@PostMapping(value = "/uploadMinio")
|
||||
public Result<?> uploadMinio(HttpServletRequest request) throws Exception {
|
||||
Result<?> result = new Result<>();
|
||||
// 获取业务路径
|
||||
String bizPath = request.getParameter("biz");
|
||||
|
||||
//LOWCOD-2580 sys/common/upload接口存在任意文件上传漏洞
|
||||
boolean flag = oConvertUtils.isNotEmpty(bizPath) && (bizPath.contains("../") || bizPath.contains("..\\"));
|
||||
if (flag) {
|
||||
throw new JeecgBootException("上传目录bizPath,格式非法!");
|
||||
}
|
||||
// 获取上传文件对象
|
||||
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
|
||||
MultipartFile file = multipartRequest.getFile("file");
|
||||
|
||||
// 文件安全校验,防止上传漏洞文件
|
||||
SsrfFileTypeFilter.checkUploadFileType(file, bizPath);
|
||||
|
||||
if(oConvertUtils.isEmpty(bizPath)){
|
||||
bizPath = "";
|
||||
}
|
||||
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
|
||||
// 获取上传文件对象
|
||||
MultipartFile file = multipartRequest.getFile("file");
|
||||
// 获取文件名
|
||||
String orgName = file.getOriginalFilename();
|
||||
orgName = CommonUtils.getFileName(orgName);
|
||||
|
||||
@ -1686,6 +1686,42 @@ public class SysUserController {
|
||||
return Result.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加用户【后台租户模式专用,敲敲云不要用这个】
|
||||
*
|
||||
* @param jsonObject
|
||||
* @return
|
||||
*/
|
||||
@RequiresPermissions("system:user:addTenantUser")
|
||||
@RequestMapping(value = "/addTenantUser", method = RequestMethod.POST)
|
||||
public Result<SysUser> addTenantUser(@RequestBody JSONObject jsonObject) {
|
||||
Result<SysUser> result = new Result<SysUser>();
|
||||
String selectedRoles = jsonObject.getString("selectedroles");
|
||||
String selectedDeparts = jsonObject.getString("selecteddeparts");
|
||||
try {
|
||||
SysUser user = JSON.parseObject(jsonObject.toJSONString(), SysUser.class);
|
||||
user.setCreateTime(new Date());//设置创建时间
|
||||
String salt = oConvertUtils.randomGen(8);
|
||||
user.setSalt(salt);
|
||||
String passwordEncode = PasswordUtil.encrypt(user.getUsername(), user.getPassword(), salt);
|
||||
user.setPassword(passwordEncode);
|
||||
user.setStatus(1);
|
||||
user.setDelFlag(CommonConstant.DEL_FLAG_0);
|
||||
//用户表字段org_code不能在这里设置他的值
|
||||
user.setOrgCode(null);
|
||||
// 保存用户走一个service 保证事务
|
||||
//获取租户ids
|
||||
String relTenantIds = jsonObject.getString("relTenantIds");
|
||||
sysUserService.saveUser(user, selectedRoles, selectedDeparts, relTenantIds, true);
|
||||
baseCommonService.addLog("添加用户,username: " + user.getUsername(), CommonConstant.LOG_TYPE_2, 2);
|
||||
result.success("添加成功!");
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
result.error500("操作失败");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改租户下的用户【低代码应用专用接口】
|
||||
* @param sysUser
|
||||
|
||||
@ -2055,7 +2055,10 @@ public class SysBaseApiImpl implements ISysBaseAPI {
|
||||
if(oConvertUtils.isEmpty(code)) {
|
||||
return null;
|
||||
}
|
||||
List<SysDepart> list = sysDepartList.stream().filter(sysDepart -> sysDepart.getOrgCode().equals(code)).toList();
|
||||
List<SysDepart> list = sysDepartList.stream()
|
||||
.filter(sysDepart -> sysDepart.getOrgCode().equals(code))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
//判断去上级的级别
|
||||
if(!CollectionUtils.isEmpty(list) && nowLevel == level) {
|
||||
return list.get(0);
|
||||
|
||||
@ -15,6 +15,7 @@ import org.jeecg.common.system.api.ISysBaseAPI;
|
||||
import org.jeecg.common.system.vo.SysFilesModel;
|
||||
import org.jeecg.common.util.CommonUtils;
|
||||
import org.jeecg.common.util.RedisUtil;
|
||||
import org.jeecg.common.util.filter.SsrfFileTypeFilter;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.system.entity.SysComment;
|
||||
import org.jeecg.modules.system.entity.SysFormFile;
|
||||
@ -119,28 +120,24 @@ public class SysCommentServiceImpl extends ServiceImpl<SysCommentMapper, SysComm
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void saveOneFileComment(HttpServletRequest request) {
|
||||
|
||||
//update-begin-author:taoyan date:2023-6-12 for: QQYUN-4310【文件】从文件库选择文件功能未做
|
||||
String existFileId = request.getParameter("fileId");
|
||||
if(oConvertUtils.isEmpty(existFileId)){
|
||||
String savePath = "";
|
||||
// 获取业务路径
|
||||
String bizPath = request.getParameter("biz");
|
||||
//LOWCOD-2580 sys/common/upload接口存在任意文件上传漏洞
|
||||
if (oConvertUtils.isNotEmpty(bizPath)) {
|
||||
if (bizPath.contains(SymbolConstant.SPOT_SINGLE_SLASH) || bizPath.contains(SymbolConstant.SPOT_DOUBLE_BACKSLASH)) {
|
||||
throw new JeecgBootException("上传目录bizPath,格式非法!");
|
||||
}
|
||||
}
|
||||
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
|
||||
// 获取上传文件对象
|
||||
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
|
||||
MultipartFile file = multipartRequest.getFile("file");
|
||||
|
||||
// 文件安全校验,防止上传漏洞文件
|
||||
try {
|
||||
SsrfFileTypeFilter.checkUploadFileType(file, bizPath);
|
||||
} catch (Exception e) {
|
||||
throw new JeecgBootException(e);
|
||||
}
|
||||
|
||||
if (oConvertUtils.isEmpty(bizPath)) {
|
||||
if (CommonConstant.UPLOAD_TYPE_OSS.equals(uploadType)) {
|
||||
//未指定目录,则用阿里云默认目录 upload
|
||||
bizPath = "upload";
|
||||
} else {
|
||||
bizPath = "";
|
||||
}
|
||||
bizPath = CommonConstant.UPLOAD_TYPE_OSS.equals(uploadType) ? "upload" : "";
|
||||
}
|
||||
if (CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)) {
|
||||
savePath = this.uploadLocal(file, bizPath);
|
||||
@ -348,10 +345,13 @@ public class SysCommentServiceImpl extends ServiceImpl<SysCommentMapper, SysComm
|
||||
* @return
|
||||
*/
|
||||
private String uploadLocal(MultipartFile mf, String bizPath) {
|
||||
//LOWCOD-2580 sys/common/upload接口存在任意文件上传漏洞
|
||||
if (oConvertUtils.isNotEmpty(bizPath) && (bizPath.contains("../") || bizPath.contains("..\\"))) {
|
||||
throw new JeecgBootException("上传目录bizPath,格式非法!");
|
||||
try {
|
||||
// 文件安全校验,防止上传漏洞文件
|
||||
SsrfFileTypeFilter.checkUploadFileType(mf, bizPath);
|
||||
} catch (Exception e) {
|
||||
throw new JeecgBootException(e);
|
||||
}
|
||||
|
||||
try {
|
||||
String ctxPath = uploadpath;
|
||||
String fileName = null;
|
||||
|
||||
@ -785,7 +785,7 @@ public class SysDepartServiceImpl extends ServiceImpl<SysDepartMapper, SysDepart
|
||||
List<String> departIdList = new ArrayList<>();
|
||||
//如果前端传过来的部门id不为空的时候,说明是系统用户根据所属部门选择主岗位或者兼职岗位
|
||||
if(oConvertUtils.isNotEmpty(departIds) && oConvertUtils.isEmpty(parentId)){
|
||||
departIdList = list.stream().map(SysDepart::getId).toList();
|
||||
departIdList = list.stream().map(SysDepart::getId).collect(Collectors.toList());
|
||||
}
|
||||
List<SysDepartTreeModel> records = new ArrayList<>();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
@ -1859,7 +1859,7 @@ public class SysDepartServiceImpl extends ServiceImpl<SysDepartMapper, SysDepart
|
||||
List<SysDepart> sysDepartList = departMapper.selectList(query);
|
||||
if(!CollectionUtils.isEmpty(sysDepartList)){
|
||||
//获取部门名称拼接返回给前台
|
||||
List<String> departNameList = sysDepartList.stream().map(SysDepart::getDepartName).toList();
|
||||
List<String> departNameList = sysDepartList.stream().map(SysDepart::getDepartName).collect(Collectors.toList());
|
||||
String departNames = String.join("/", departNameList);
|
||||
redisUtil.set(CommonConstant.DEPART_NAME_REDIS_KEY_PRE + orgCode,departNames);
|
||||
return departNames;
|
||||
|
||||
@ -100,11 +100,11 @@ public class SysTenantPackServiceImpl extends ServiceImpl<SysTenantPackMapper, S
|
||||
// 提取已存在的用户ID
|
||||
List<String> existingUserIds = existingUsers.stream()
|
||||
.map(SysTenantPackUser::getUserId)
|
||||
.toList();
|
||||
.collect(Collectors.toList());
|
||||
// 过滤出需要新增的用户ID
|
||||
List<String> newUserIds = userIds.stream()
|
||||
.filter(userId -> !existingUserIds.contains(userId))
|
||||
.toList();
|
||||
.collect(Collectors.toList());
|
||||
for (String userId : newUserIds) {
|
||||
//update-end---author:wangshuai---date:2025-09-03---for: 编辑时需要查看有没有未分配的用户---
|
||||
SysTenantPackUser tenantPackUser = new SysTenantPackUser(tenantId, packId, userId);
|
||||
|
||||
@ -2734,7 +2734,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
public IPage<SysUserSysDepPostModel> queryDepartPostUserByOrgCode(String orgCode, SysUser userParams, IPage page) {
|
||||
List<SysUserSysDepPostModel> sysDepartModels = baseMapper.queryDepartPostUserByOrgCode(page, orgCode, userParams);
|
||||
if(CollectionUtil.isNotEmpty(sysDepartModels)){
|
||||
List<String> userIds = sysDepartModels.stream().map(SysUserSysDepPostModel::getId).toList();
|
||||
List<String> userIds = sysDepartModels.stream().map(SysUserSysDepPostModel::getId).collect(Collectors.toList());
|
||||
//获取部门名称
|
||||
Map<String, String> useDepNames = this.getDepNamesByUserIds(userIds);
|
||||
sysDepartModels.forEach(item -> {
|
||||
|
||||
@ -266,11 +266,11 @@ jeecg:
|
||||
#xxl-job配置
|
||||
xxljob:
|
||||
enabled: false
|
||||
adminAddresses: http://127.0.0.1:9080/xxl-job-admin
|
||||
adminAddresses: http://jeecg-boot-xxljob:9080/xxl-job-admin
|
||||
appname: ${spring.application.name}
|
||||
accessToken: ''
|
||||
address: 127.0.0.1:30007
|
||||
ip: 127.0.0.1
|
||||
address: jeecg-boot-xxljob:30007
|
||||
ip: jeecg-boot-xxljob
|
||||
port: 30007
|
||||
logPath: logs/jeecg/job/jobhandler/
|
||||
logRetentionDays: 30
|
||||
|
||||
@ -12,5 +12,5 @@ EXPOSE 9080
|
||||
|
||||
ADD ./target/jeecg-cloud-xxljob-3.8.3.jar ./
|
||||
|
||||
CMD java -Dfile.encoding=utf-8 -Djava.security.egd=file:/dev/./urandom -jar jeecg-cloud-xxljob-3.8.3.jar
|
||||
CMD sleep 60;java -Dfile.encoding=utf-8 -Djava.security.egd=file:/dev/./urandom -jar jeecg-cloud-xxljob-3.8.3.jar
|
||||
|
||||
|
||||
@ -61,15 +61,14 @@
|
||||
|
||||
<!-- 积木报表-->
|
||||
<jimureport-spring-boot-starter.version>2.1.3</jimureport-spring-boot-starter.version>
|
||||
<jimubi-spring-boot-starter.version>2.1.3</jimubi-spring-boot-starter.version>
|
||||
<minidao.version>1.10.14</minidao.version>
|
||||
<jimubi-spring-boot-starter.version>2.1.4</jimubi-spring-boot-starter.version>
|
||||
<minidao.version>1.10.16</minidao.version>
|
||||
<autopoi-web.version>1.4.18</autopoi-web.version>
|
||||
|
||||
<!-- 持久层 -->
|
||||
<mybatis-plus.version>3.5.12</mybatis-plus.version>
|
||||
<dynamic-datasource-spring-boot-starter.version>4.1.3</dynamic-datasource-spring-boot-starter.version>
|
||||
<druid.version>1.2.24</druid.version>
|
||||
<minidao.version>1.10.14</minidao.version>
|
||||
|
||||
<commons-io.version>2.11.0</commons-io.version>
|
||||
<commons-fileupload.version>1.5</commons-fileupload.version>
|
||||
@ -269,7 +268,7 @@
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<artifactId>hibernate-re</artifactId>
|
||||
<version>3.8.2-beta</version>
|
||||
<version>3.8.2.2</version>
|
||||
</dependency>
|
||||
|
||||
<!--mongon db-->
|
||||
@ -494,7 +493,7 @@
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<artifactId>jeecg-boot-starter-chatgpt</artifactId>
|
||||
<version>${jeecgboot.version}</version>
|
||||
<version>3.8.3.1</version>
|
||||
</dependency>
|
||||
<!--flyway 支持 mysql5.7+、MariaDB10.3.16-->
|
||||
<!--mysql5.6,需要把版本号改成5.2.1-->
|
||||
|
||||
@ -35,9 +35,9 @@ JeecgBoot-Vue3采用 Vue3.0、Vite、 Ant-Design-Vue4、TypeScript 等新技术
|
||||
## 安装与使用
|
||||
|
||||
* 本地环境安装 `Node.js 、npm 、pnpm`
|
||||
* Node.js 版本建议`v20.15.0`,要求`Node 20+` 版本以上
|
||||
* Node.js 版本要求`Node 20+` 版本以上
|
||||
|
||||
` ( 因为Vite5 不再支持已 EOL 的 Node.js 14 / 16 / 17 / 19,现在需要 Node.js 18 / 20+ )`
|
||||
` ( Vite 不再支持已结束生命周期(EOL)的 Node.js 18。现在需要使用 Node.js 20.19+ 或 22.12+)`
|
||||
|
||||
|
||||
|
||||
|
||||
@ -107,7 +107,7 @@
|
||||
"@types/sortablejs": "^1.15.8",
|
||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||
"@typescript-eslint/parser": "^6.21.0",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"@vitejs/plugin-vue": "5.2.1",
|
||||
"@vitejs/plugin-vue-jsx": "4.1.1",
|
||||
"@vue/compiler-sfc": "^3.5.13",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
@ -141,8 +141,8 @@
|
||||
"prettier": "^3.4.2",
|
||||
"pretty-quick": "^4.0.0",
|
||||
"rimraf": "^5.0.10",
|
||||
"rollup": "^4.30.0",
|
||||
"rollup-plugin-visualizer": "^5.13.1",
|
||||
"rollup": "4.52.5",
|
||||
"rollup-plugin-visualizer": "5.14.0",
|
||||
"stylelint": "^16.12.0",
|
||||
"stylelint-config-prettier": "^9.0.5",
|
||||
"stylelint-config-recommended": "^14.0.1",
|
||||
|
||||
Reference in New Issue
Block a user