diff --git a/pom.xml b/pom.xml index 148eb1ee..694244a8 100644 --- a/pom.xml +++ b/pom.xml @@ -6,25 +6,27 @@ com.ruoyi ruoyi - 3.8.7 + 3.8.8 ruoyi http://www.ruoyi.vip 若依管理系统 - 3.8.7 + 3.8.8 UTF-8 UTF-8 1.8 3.1.1 - 1.2.20 + 5.3.33 + 5.7.12 + 1.2.23 1.21 3.0.0 2.3.3 1.4.7 2.0.43 - 6.4.13 + 6.6.3 2.13.0 4.1.2 2.3 @@ -36,6 +38,24 @@ + + + org.springframework + spring-framework-bom + ${spring-framework.version} + pom + import + + + + + org.springframework.security + spring-security-bom + ${spring-security.version} + pom + import + + org.springframework.boot diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml index efcc9e9a..3a3385f0 100644 --- a/ruoyi-admin/pom.xml +++ b/ruoyi-admin/pom.xml @@ -5,7 +5,7 @@ ruoyi com.ruoyi - 3.8.7 + 3.8.8 4.0.0 jar diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java index 41029fd9..504c0fde 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.TreeSet; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisTemplate; @@ -80,7 +81,7 @@ public class CacheController public AjaxResult getCacheKeys(@PathVariable String cacheName) { Set cacheKeys = redisTemplate.keys(cacheName + "*"); - return AjaxResult.success(cacheKeys); + return AjaxResult.success(new TreeSet<>(cacheKeys)); } @PreAuthorize("@ss.hasPermi('monitor:cache:list')") diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java index dc29d498..24aafae7 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java @@ -124,6 +124,8 @@ public class SysUserController extends BaseController @PostMapping public AjaxResult add(@Validated @RequestBody SysUser user) { + deptService.checkDeptDataScope(user.getDeptId()); + roleService.checkRoleDataScope(user.getRoleIds()); if (!userService.checkUserNameUnique(user)) { return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在"); @@ -151,6 +153,8 @@ public class SysUserController extends BaseController { userService.checkUserAllowed(user); userService.checkUserDataScope(user.getUserId()); + deptService.checkDeptDataScope(user.getDeptId()); + roleService.checkRoleDataScope(user.getRoleIds()); if (!userService.checkUserNameUnique(user)) { return error("修改用户'" + user.getUserName() + "'失败,登录账号已存在"); @@ -235,6 +239,7 @@ public class SysUserController extends BaseController public AjaxResult insertAuthRole(Long userId, Long[] roleIds) { userService.checkUserDataScope(userId); + roleService.checkRoleDataScope(roleIds); userService.insertUserAuth(userId, roleIds); return success(); } diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index 5cf3612a..9312816a 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -3,7 +3,7 @@ ruoyi: # 名称 name: RuoYi # 版本 - version: 3.8.7 + version: 3.8.8 # 版权年份 copyrightYear: 2024 # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index 38326a1f..56c3f819 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -5,7 +5,7 @@ ruoyi com.ruoyi - 3.8.7 + 3.8.8 4.0.0 @@ -59,13 +59,6 @@ jackson-databind - - - com.baomidou - dynamic-datasource-spring-boot-starter - 3.5.2 - - com.alibaba.fastjson2 diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java index d534a3eb..0d69d394 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java @@ -88,6 +88,11 @@ public @interface Excel */ public String[] combo() default {}; + /** + * 是否从字典读数据到combo,默认不读取,如读取需要设置dictType注解. + */ + public boolean comboReadDict() default false; + /** * 是否需要纵向合并单元格,应对需求:含有list集合单元格) */ @@ -171,7 +176,7 @@ public @interface Excel public enum ColumnType { - NUMERIC(0), STRING(1), IMAGE(2); + NUMERIC(0), STRING(1), IMAGE(2), TEXT(3); private final int value; ColumnType(int value) diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Sensitive.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Sensitive.java new file mode 100644 index 00000000..c0621e9e --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Sensitive.java @@ -0,0 +1,24 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.ruoyi.common.config.serializer.SensitiveJsonSerializer; +import com.ruoyi.common.enums.DesensitizedType; + +/** + * 数据脱敏注解 + * + * @author ruoyi + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@JacksonAnnotationsInside +@JsonSerialize(using = SensitiveJsonSerializer.class) +public @interface Sensitive +{ + DesensitizedType desensitizedType(); +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/config/serializer/SensitiveJsonSerializer.java b/ruoyi-common/src/main/java/com/ruoyi/common/config/serializer/SensitiveJsonSerializer.java new file mode 100644 index 00000000..e819a1d7 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/config/serializer/SensitiveJsonSerializer.java @@ -0,0 +1,67 @@ +package com.ruoyi.common.config.serializer; + +import java.io.IOException; +import java.util.Objects; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; +import com.ruoyi.common.annotation.Sensitive; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.enums.DesensitizedType; +import com.ruoyi.common.utils.SecurityUtils; + +/** + * 数据脱敏序列化过滤 + * + * @author ruoyi + */ +public class SensitiveJsonSerializer extends JsonSerializer implements ContextualSerializer +{ + private DesensitizedType desensitizedType; + + @Override + public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException + { + if (desensitization()) + { + gen.writeString(desensitizedType.desensitizer().apply(value)); + } + else + { + gen.writeString(value); + } + } + + @Override + public JsonSerializer createContextual(SerializerProvider prov, BeanProperty property) + throws JsonMappingException + { + Sensitive annotation = property.getAnnotation(Sensitive.class); + if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass())) + { + this.desensitizedType = annotation.desensitizedType(); + return this; + } + return prov.findValueSerializer(property.getType(), property); + } + + /** + * 是否需要脱敏处理 + */ + private boolean desensitization() + { + try + { + LoginUser securityUser = SecurityUtils.getLoginUser(); + // 管理员不脱敏 + return !securityUser.getUser().isAdmin(); + } + catch (Exception e) + { + return true; + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java index 7acadb9b..a94c3fa3 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java @@ -1,5 +1,6 @@ package com.ruoyi.common.constant; +import java.util.Locale; import io.jsonwebtoken.Claims; /** @@ -19,6 +20,11 @@ public class Constants */ public static final String GBK = "GBK"; + /** + * 系统语言 + */ + public static final Locale DEFAULT_LOCALE = Locale.SIMPLIFIED_CHINESE; + /** * www主域 */ diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java index 9f3a6f65..f0621202 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java @@ -42,6 +42,9 @@ public class SysMenu extends BaseEntity /** 路由参数 */ private String query; + /** 路由名称,默认和路由地址相同的驼峰格式(注意:因为vue3版本的router会删除名称相同路由,为避免名字的冲突,特殊情况可以自定义) */ + private String routeName; + /** 是否为外链(0是 1否) */ private String isFrame; @@ -53,7 +56,7 @@ public class SysMenu extends BaseEntity /** 显示状态(0显示 1隐藏) */ private String visible; - + /** 菜单状态(0正常 1停用) */ private String status; @@ -151,6 +154,16 @@ public class SysMenu extends BaseEntity this.query = query; } + public String getRouteName() + { + return routeName; + } + + public void setRouteName(String routeName) + { + this.routeName = routeName; + } + public String getIsFrame() { return isFrame; @@ -232,7 +245,7 @@ public class SysMenu extends BaseEntity { this.children = children; } - + @Override public String toString() { return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) @@ -242,6 +255,8 @@ public class SysMenu extends BaseEntity .append("orderNum", getOrderNum()) .append("path", getPath()) .append("component", getComponent()) + .append("query", getQuery()) + .append("routeName", getRouteName()) .append("isFrame", getIsFrame()) .append("IsCache", getIsCache()) .append("menuType", getMenuType()) diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java index 8d4d4c56..c33d9124 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java @@ -22,7 +22,7 @@ public class SysUser extends BaseEntity private static final long serialVersionUID = 1L; /** 用户ID */ - @Excel(name = "用户序号", cellType = ColumnType.NUMERIC, prompt = "用户编号") + @Excel(name = "用户序号", type = Type.EXPORT, cellType = ColumnType.NUMERIC, prompt = "用户编号") private Long userId; /** 部门ID */ @@ -42,7 +42,7 @@ public class SysUser extends BaseEntity private String email; /** 手机号码 */ - @Excel(name = "手机号码") + @Excel(name = "手机号码", cellType = ColumnType.TEXT) private String phonenumber; /** 用户性别 */ diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java index 8a2ad80a..938d0c9f 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java @@ -365,6 +365,10 @@ public class Convert */ public static String[] toStrArray(String str) { + if (StringUtils.isEmpty(str)) + { + return new String[] {}; + } return toStrArray(",", str); } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/DesensitizedType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DesensitizedType.java new file mode 100644 index 00000000..45081229 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DesensitizedType.java @@ -0,0 +1,59 @@ +package com.ruoyi.common.enums; + +import java.util.function.Function; +import com.ruoyi.common.utils.DesensitizedUtil; + +/** + * 脱敏类型 + * + * @author ruoyi + */ +public enum DesensitizedType +{ + /** + * 姓名,第2位星号替换 + */ + USERNAME(s -> s.replaceAll("(\\S)\\S(\\S*)", "$1*$2")), + + /** + * 密码,全部字符都用*代替 + */ + PASSWORD(DesensitizedUtil::password), + + /** + * 身份证,中间10位星号替换 + */ + ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\d{4})", "$1** **** ****$2")), + + /** + * 手机号,中间4位星号替换 + */ + PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")), + + /** + * 电子邮箱,仅显示第一个字母和@后面的地址显示,其他星号替换 + */ + EMAIL(s -> s.replaceAll("(^.)[^@]*(@.*$)", "$1****$2")), + + /** + * 银行卡号,保留最后4位,其他星号替换 + */ + BANK_CARD(s -> s.replaceAll("\\d{15}(\\d{3})", "**** **** **** **** $1")), + + /** + * 车牌号码,包含普通车辆、新能源车辆 + */ + CAR_LICENSE(DesensitizedUtil::carLicense); + + private final Function desensitizer; + + DesensitizedType(Function desensitizer) + { + this.desensitizer = desensitizer; + } + + public Function desensitizer() + { + return desensitizer; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java index 23a76fe1..b4eaabcb 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java @@ -32,10 +32,10 @@ public class XssFilter implements Filter String tempExcludes = filterConfig.getInitParameter("excludes"); if (StringUtils.isNotEmpty(tempExcludes)) { - String[] url = tempExcludes.split(","); - for (int i = 0; url != null && i < url.length; i++) + String[] urls = tempExcludes.split(","); + for (String url : urls) { - excludes.add(url[i]); + excludes.add(url); } } } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DesensitizedUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DesensitizedUtil.java new file mode 100644 index 00000000..f8a4c022 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DesensitizedUtil.java @@ -0,0 +1,49 @@ +package com.ruoyi.common.utils; + +/** + * 脱敏工具类 + * + * @author ruoyi + */ +public class DesensitizedUtil +{ + /** + * 密码的全部字符都用*代替,比如:****** + * + * @param password 密码 + * @return 脱敏后的密码 + */ + public static String password(String password) + { + if (StringUtils.isBlank(password)) + { + return StringUtils.EMPTY; + } + return StringUtils.repeat('*', password.length()); + } + + /** + * 车牌中间用*代替,如果是错误的车牌,不处理 + * + * @param carLicense 完整的车牌号 + * @return 脱敏后的车牌 + */ + public static String carLicense(String carLicense) + { + if (StringUtils.isBlank(carLicense)) + { + return StringUtils.EMPTY; + } + // 普通车牌 + if (carLicense.length() == 7) + { + carLicense = StringUtils.hide(carLicense, 3, 6); + } + else if (carLicense.length() == 8) + { + // 新能源车牌 + carLicense = StringUtils.hide(carLicense, 3, 7); + } + return carLicense; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java index 39ad84a6..8204f133 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java @@ -56,6 +56,10 @@ public class DictUtils */ public static String getDictLabel(String dictType, String dictValue) { + if (StringUtils.isEmpty(dictValue)) + { + return StringUtils.EMPTY; + } return getDictLabel(dictType, dictValue, SEPARATOR); } @@ -68,6 +72,10 @@ public class DictUtils */ public static String getDictValue(String dictType, String dictLabel) { + if (StringUtils.isEmpty(dictLabel)) + { + return StringUtils.EMPTY; + } return getDictValue(dictType, dictLabel, SEPARATOR); } @@ -83,31 +91,31 @@ public class DictUtils { StringBuilder propertyString = new StringBuilder(); List datas = getDictCache(dictType); - - if (StringUtils.isNotNull(datas)) + if (StringUtils.isNull(datas)) { - if (StringUtils.containsAny(separator, dictValue)) + return StringUtils.EMPTY; + } + if (StringUtils.containsAny(separator, dictValue)) + { + for (SysDictData dict : datas) { - for (SysDictData dict : datas) + for (String value : dictValue.split(separator)) { - for (String value : dictValue.split(separator)) + if (value.equals(dict.getDictValue())) { - if (value.equals(dict.getDictValue())) - { - propertyString.append(dict.getDictLabel()).append(separator); - break; - } + propertyString.append(dict.getDictLabel()).append(separator); + break; } } } - else + } + else + { + for (SysDictData dict : datas) { - for (SysDictData dict : datas) + if (dictValue.equals(dict.getDictValue())) { - if (dictValue.equals(dict.getDictValue())) - { - return dict.getDictLabel(); - } + return dict.getDictLabel(); } } } @@ -126,8 +134,11 @@ public class DictUtils { StringBuilder propertyString = new StringBuilder(); List datas = getDictCache(dictType); - - if (StringUtils.containsAny(separator, dictLabel) && StringUtils.isNotEmpty(datas)) + if (StringUtils.isNull(datas)) + { + return StringUtils.EMPTY; + } + if (StringUtils.containsAny(separator, dictLabel)) { for (SysDictData dict : datas) { @@ -154,6 +165,48 @@ public class DictUtils return StringUtils.stripEnd(propertyString.toString(), separator); } + /** + * 根据字典类型获取字典所有值 + * + * @param dictType 字典类型 + * @return 字典值 + */ + public static String getDictValues(String dictType) + { + StringBuilder propertyString = new StringBuilder(); + List datas = getDictCache(dictType); + if (StringUtils.isNull(datas)) + { + return StringUtils.EMPTY; + } + for (SysDictData dict : datas) + { + propertyString.append(dict.getDictValue()).append(SEPARATOR); + } + return StringUtils.stripEnd(propertyString.toString(), SEPARATOR); + } + + /** + * 根据字典类型获取字典所有标签 + * + * @param dictType 字典类型 + * @return 字典值 + */ + public static String getDictLabels(String dictType) + { + StringBuilder propertyString = new StringBuilder(); + List datas = getDictCache(dictType); + if (StringUtils.isNull(datas)) + { + return StringUtils.EMPTY; + } + for (SysDictData dict : datas) + { + propertyString.append(dict.getDictLabel()).append(SEPARATOR); + } + return StringUtils.stripEnd(propertyString.toString(), SEPARATOR); + } + /** * 删除指定字典缓存 * diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java index 215d1153..fc6c6b5e 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java @@ -23,6 +23,9 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** 下划线 */ private static final char SEPARATOR = '_'; + /** 星号 */ + private static final char ASTERISK = '*'; + /** * 获取参数不为空值 * @@ -163,6 +166,49 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils return (str == null ? "" : str.trim()); } + /** + * 替换指定字符串的指定区间内字符为"*" + * + * @param str 字符串 + * @param startInclude 开始位置(包含) + * @param endExclude 结束位置(不包含) + * @return 替换后的字符串 + */ + public static String hide(CharSequence str, int startInclude, int endExclude) + { + if (isEmpty(str)) + { + return NULLSTR; + } + final int strLength = str.length(); + if (startInclude > strLength) + { + return NULLSTR; + } + if (endExclude > strLength) + { + endExclude = strLength; + } + if (startInclude > endExclude) + { + // 如果起始位置大于结束位置,不替换 + return NULLSTR; + } + final char[] chars = new char[strLength]; + for (int i = 0; i < strLength; i++) + { + if (i >= startInclude && i < endExclude) + { + chars[i] = ASTERISK; + } + else + { + chars[i] = str.charAt(i); + } + } + return new String(chars); + } + /** * 截取字符串 * diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java index 4652a298..5a0ef640 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java @@ -25,7 +25,7 @@ public class FileUploadUtils /** * 默认大小 50M */ - public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024; + public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024L; /** * 默认的文件名最大长度 100 diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java index 68ccbf11..64f97baa 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java @@ -39,6 +39,7 @@ import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.ClientAnchor; +import org.apache.poi.ss.usermodel.DataFormat; import org.apache.poi.ss.usermodel.DataValidation; import org.apache.poi.ss.usermodel.DataValidationConstraint; import org.apache.poi.ss.usermodel.DataValidationHelper; @@ -470,7 +471,12 @@ public class ExcelUtil } else if (StringUtils.isNotEmpty(attr.dictType())) { - val = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator()); + if (!sysDictMap.containsKey(attr.dictType() + val)) + { + String dictValue = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator()); + sysDictMap.put(attr.dictType() + val, dictValue); + } + val = sysDictMap.get(attr.dictType() + val); } else if (!attr.handler().equals(ExcelHandlerAdapter.class)) { @@ -783,6 +789,8 @@ public class ExcelUtil titleFont.setFontHeightInPoints((short) 16); titleFont.setBold(true); style.setFont(titleFont); + DataFormat dataFormat = wb.createDataFormat(); + style.setDataFormat(dataFormat.getFormat("@")); styles.put("title", style); style = wb.createCellStyle(); @@ -845,6 +853,9 @@ public class ExcelUtil headerFont.setBold(true); headerFont.setColor(excel.headerColor().index); style.setFont(headerFont); + // 设置表格头单元格文本形式 + DataFormat dataFormat = wb.createDataFormat(); + style.setDataFormat(dataFormat.getFormat("@")); headerStyles.put(key, style); } } @@ -862,34 +873,66 @@ public class ExcelUtil Map styles = new HashMap(); for (Object[] os : fields) { + Field field = (Field) os[0]; Excel excel = (Excel) os[1]; - String key = StringUtils.format("data_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor()); - if (!styles.containsKey(key)) + if (Collection.class.isAssignableFrom(field.getType())) { - CellStyle style = wb.createCellStyle(); - style.setAlignment(excel.align()); - style.setVerticalAlignment(VerticalAlignment.CENTER); - style.setBorderRight(BorderStyle.THIN); - style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); - style.setBorderLeft(BorderStyle.THIN); - style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); - style.setBorderTop(BorderStyle.THIN); - style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); - style.setBorderBottom(BorderStyle.THIN); - style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); - style.setFillPattern(FillPatternType.SOLID_FOREGROUND); - style.setFillForegroundColor(excel.backgroundColor().getIndex()); - Font dataFont = wb.createFont(); - dataFont.setFontName("Arial"); - dataFont.setFontHeightInPoints((short) 10); - dataFont.setColor(excel.color().index); - style.setFont(dataFont); - styles.put(key, style); + ParameterizedType pt = (ParameterizedType) field.getGenericType(); + Class subClass = (Class) pt.getActualTypeArguments()[0]; + List subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class); + for (Field subField : subFields) + { + Excel subExcel = subField.getAnnotation(Excel.class); + annotationDataStyles(styles, subField, subExcel); + } + } + else + { + annotationDataStyles(styles, field, excel); } } return styles; } + /** + * 根据Excel注解创建表格列样式 + * + * @param styles 自定义样式列表 + * @param field 属性列信息 + * @param excel 注解信息 + */ + public void annotationDataStyles(Map styles, Field field, Excel excel) + { + String key = StringUtils.format("data_{}_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor(), excel.cellType()); + if (!styles.containsKey(key)) + { + CellStyle style = wb.createCellStyle(); + style.setAlignment(excel.align()); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderRight(BorderStyle.THIN); + style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderLeft(BorderStyle.THIN); + style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderTop(BorderStyle.THIN); + style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderBottom(BorderStyle.THIN); + style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + style.setFillForegroundColor(excel.backgroundColor().getIndex()); + Font dataFont = wb.createFont(); + dataFont.setFontName("Arial"); + dataFont.setFontHeightInPoints((short) 10); + dataFont.setColor(excel.color().index); + style.setFont(dataFont); + if (ColumnType.TEXT == excel.cellType()) + { + DataFormat dataFormat = wb.createDataFormat(); + style.setDataFormat(dataFormat.getFormat("@")); + } + styles.put(key, style); + } + } + /** * 创建单元格 */ @@ -904,7 +947,7 @@ public class ExcelUtil if (isSubList()) { // 填充默认样式,防止合并单元格样式失效 - sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor()))); + sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType()))); if (attr.needMerge()) { sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column)); @@ -922,7 +965,7 @@ public class ExcelUtil */ public void setCellVo(Object value, Excel attr, Cell cell) { - if (ColumnType.STRING == attr.cellType()) + if (ColumnType.STRING == attr.cellType() || ColumnType.TEXT == attr.cellType()) { String cellValue = Convert.toStr(value); // 对于任何以表达式触发字符 =-+@开头的单元格,直接使用tab字符作为前缀,防止CSV注入。 @@ -999,17 +1042,28 @@ public class ExcelUtil // 设置列宽 sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256)); } - if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0) + if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0 || attr.comboReadDict()) { - if (attr.combo().length > 15 || StringUtils.join(attr.combo()).length() > 255) + String[] comboArray = attr.combo(); + if (attr.comboReadDict()) + { + if (!sysDictMap.containsKey("combo_" + attr.dictType())) + { + String labels = DictUtils.getDictLabels(attr.dictType()); + sysDictMap.put("combo_" + attr.dictType(), labels); + } + String val = sysDictMap.get("combo_" + attr.dictType()); + comboArray = StringUtils.split(val, DictUtils.SEPARATOR); + } + if (comboArray.length > 15 || StringUtils.join(comboArray).length() > 255) { // 如果下拉数大于15或字符串长度大于255,则使用一个新sheet存储,避免生成的模板下拉值获取不到 - setXSSFValidationWithHidden(sheet, attr.combo(), attr.prompt(), 1, 100, column, column); + setXSSFValidationWithHidden(sheet, comboArray, attr.prompt(), 1, 100, column, column); } else { // 提示信息或只能选择不能输入的列内容. - setPromptOrValidation(sheet, attr.combo(), attr.prompt(), 1, 100, column, column); + setPromptOrValidation(sheet, comboArray, attr.prompt(), 1, 100, column, column); } } } @@ -1034,7 +1088,7 @@ public class ExcelUtil CellRangeAddress cellAddress = new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column); sheet.addMergedRegion(cellAddress); } - cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor()))); + cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType()))); // 用于读取对象中的属性 Object value = getTargetValue(vo, field, attr); diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java index 9f40118c..e345cb45 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java @@ -13,7 +13,7 @@ public class SqlUtil /** * 定义常用的 sql关键字 */ - public static String SQL_REGEX = "and |extractvalue|updatexml|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |+|user()"; + public static String SQL_REGEX = "and |extractvalue|updatexml|sleep|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()"; /** * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) diff --git a/ruoyi-framework/pom.xml b/ruoyi-framework/pom.xml index 0bcd7d07..de50de35 100644 --- a/ruoyi-framework/pom.xml +++ b/ruoyi-framework/pom.xml @@ -5,7 +5,7 @@ ruoyi com.ruoyi - 3.8.7 + 3.8.8 4.0.0 diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java index 35a6b50d..1543ec5b 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java @@ -92,16 +92,22 @@ public class DataScopeAspect { StringBuilder sqlString = new StringBuilder(); List conditions = new ArrayList(); + List scopeCustomIds = new ArrayList(); + user.getRoles().forEach(role -> { + if (DATA_SCOPE_CUSTOM.equals(role.getDataScope()) && StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))) + { + scopeCustomIds.add(Convert.toStr(role.getRoleId())); + } + }); for (SysRole role : user.getRoles()) { String dataScope = role.getDataScope(); - if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope)) + if (conditions.contains(dataScope)) { continue; } - if (StringUtils.isNotEmpty(permission) && StringUtils.isNotEmpty(role.getPermissions()) - && !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))) + if (!StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))) { continue; } @@ -113,9 +119,15 @@ public class DataScopeAspect } else if (DATA_SCOPE_CUSTOM.equals(dataScope)) { - sqlString.append(StringUtils.format( - " OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, - role.getRoleId())); + if (scopeCustomIds.size() > 1) + { + // 多个自定数据权限使用in查询,避免多次拼接。 + sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id in ({}) ) ", deptAlias, String.join(",", scopeCustomIds))); + } + else + { + sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, role.getRoleId())); + } } else if (DATA_SCOPE_DEPT.equals(dataScope)) { @@ -123,9 +135,7 @@ public class DataScopeAspect } else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) { - sqlString.append(StringUtils.format( - " OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", - deptAlias, user.getDeptId(), user.getDeptId())); + sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", deptAlias, user.getDeptId(), user.getDeptId())); } else if (DATA_SCOPE_SELF.equals(dataScope)) { @@ -142,7 +152,7 @@ public class DataScopeAspect conditions.add(dataScope); } - // 多角色情况下,所有角色都不包含传递过来的权限字符,这个时候sqlString也会为空,所以要限制一下,不查询任何数据 + // 角色都不包含传递过来的权限字符,这个时候sqlString也会为空,所以要限制一下,不查询任何数据 if (StringUtils.isEmpty(conditions)) { sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias)); diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/I18nConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/I18nConfig.java new file mode 100644 index 00000000..163fd01c --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/I18nConfig.java @@ -0,0 +1,43 @@ +package com.ruoyi.framework.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; +import org.springframework.web.servlet.i18n.SessionLocaleResolver; +import com.ruoyi.common.constant.Constants; + +/** + * 资源文件配置加载 + * + * @author ruoyi + */ +@Configuration +public class I18nConfig implements WebMvcConfigurer +{ + @Bean + public LocaleResolver localeResolver() + { + SessionLocaleResolver slr = new SessionLocaleResolver(); + // 默认语言 + slr.setDefaultLocale(Constants.DEFAULT_LOCALE); + return slr; + } + + @Bean + public LocaleChangeInterceptor localeChangeInterceptor() + { + LocaleChangeInterceptor lci = new LocaleChangeInterceptor(); + // 参数名 + lci.setParamName("lang"); + return lci; + } + + @Override + public void addInterceptors(InterceptorRegistry registry) + { + registry.addInterceptor(localeChangeInterceptor()); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java index bdb7199f..b04beffb 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java @@ -2,16 +2,17 @@ package com.ruoyi.framework.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.authentication.ProviderManager; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.logout.LogoutFilter; import org.springframework.web.filter.CorsFilter; @@ -25,8 +26,9 @@ import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl; * * @author ruoyi */ -@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) -public class SecurityConfig extends WebSecurityConfigurerAdapter +@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true) +@Configuration +public class SecurityConfig { /** * 自定义用户认证逻辑 @@ -65,16 +67,15 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter private PermitAllUrlProperties permitAllUrl; /** - * 解决 无法直接注入 AuthenticationManager - * - * @return - * @throws Exception + * 身份验证实现 */ @Bean - @Override - public AuthenticationManager authenticationManagerBean() throws Exception + public AuthenticationManager authenticationManager() { - return super.authenticationManagerBean(); + DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider(); + daoAuthenticationProvider.setUserDetailsService(userDetailsService); + daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder()); + return new ProviderManager(daoAuthenticationProvider); } /** @@ -92,40 +93,39 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter * rememberMe | 允许通过remember-me登录的用户访问 * authenticated | 用户登录后可访问 */ - @Override - protected void configure(HttpSecurity httpSecurity) throws Exception + @Bean + protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { - // 注解标记允许匿名访问的url - ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests(); - permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll()); - - httpSecurity - // CSRF禁用,因为不使用session - .csrf().disable() - // 禁用HTTP响应标头 - .headers().cacheControl().disable().and() - // 认证失败处理类 - .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() - // 基于token,所以不需要session - .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() - // 过滤请求 - .authorizeRequests() + return httpSecurity + // CSRF禁用,因为不使用session + .csrf(csrf -> csrf.disable()) + // 禁用HTTP响应标头 + .headers((headersCustomizer) -> { + headersCustomizer.cacheControl(cache -> cache.disable()).frameOptions(options -> options.sameOrigin()); + }) + // 认证失败处理类 + .exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler)) + // 基于token,所以不需要session + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + // 注解标记允许匿名访问的url + .authorizeHttpRequests((requests) -> { + permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll()); // 对于登录login 注册register 验证码captchaImage 允许匿名访问 - .antMatchers("/login", "/register", "/captchaImage").permitAll() - // 静态资源,可匿名访问 - .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll() - .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll() - // 除上面外的所有请求全部需要鉴权认证 - .anyRequest().authenticated() - .and() - .headers().frameOptions().disable(); - // 添加Logout filter - httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler); - // 添加JWT filter - httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); - // 添加CORS filter - httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class); - httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class); + requests.antMatchers("/login", "/register", "/captchaImage").permitAll() + // 静态资源,可匿名访问 + .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll() + .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll() + // 除上面外的所有请求全部需要鉴权认证 + .anyRequest().authenticated(); + }) + // 添加Logout filter + .logout(logout -> logout.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler)) + // 添加JWT filter + .addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class) + // 添加CORS filter + .addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class) + .addFilterBefore(corsFilter, LogoutFilter.class) + .build(); } /** @@ -136,13 +136,4 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { return new BCryptPasswordEncoder(); } - - /** - * 身份认证接口 - */ - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception - { - auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); - } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java index 9e145115..efece07f 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java @@ -15,9 +15,11 @@ import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; import com.ruoyi.common.constant.HttpStatus; import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.text.Convert; import com.ruoyi.common.exception.DemoModeException; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.html.EscapeUtil; /** * 全局异常处理器 @@ -81,8 +83,13 @@ public class GlobalExceptionHandler public AjaxResult handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) { String requestURI = request.getRequestURI(); + String value = Convert.toStr(e.getValue()); + if (StringUtils.isNotEmpty(value)) + { + value = EscapeUtil.clean(value); + } log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e); - return AjaxResult.error(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), e.getValue())); + return AjaxResult.error(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), value)); } /** diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java index 4831849d..97abcead 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java @@ -115,12 +115,12 @@ public class SysLoginService { String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, ""); String captcha = redisCache.getCacheObject(verifyKey); - redisCache.deleteObject(verifyKey); if (captcha == null) { AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"))); throw new CaptchaExpireException(); } + redisCache.deleteObject(verifyKey); if (!code.equalsIgnoreCase(captcha)) { AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"))); diff --git a/ruoyi-generator/pom.xml b/ruoyi-generator/pom.xml index 8663be6e..27d31cc6 100644 --- a/ruoyi-generator/pom.xml +++ b/ruoyi-generator/pom.xml @@ -5,7 +5,7 @@ ruoyi com.ruoyi - 3.8.7 + 3.8.8 4.0.0 diff --git a/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml b/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml index 8ec82113..8d7dd8bf 100644 --- a/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml +++ b/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml @@ -26,7 +26,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - + @@ -68,10 +68,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" AND lower(table_comment) like lower(concat('%', #{tableComment}, '%')) - AND date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d') + AND date_format(create_time,'%Y%m%d') >= date_format(#{params.beginTime},'%Y%m%d') - AND date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d') + AND date_format(create_time,'%Y%m%d') <= date_format(#{params.endTime},'%Y%m%d') @@ -79,7 +79,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - +#if($table.sub) + + +#end + insert into ${tableName} @@ -127,7 +132,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" insert into ${subTableName}(#foreach($column in $subTable.columns) $column.columnName#if($foreach.count != $subTable.columns.size()),#end#end) values - + (#foreach($column in $subTable.columns) #{item.$column.javaField}#if($foreach.count != $subTable.columns.size()),#end#end) diff --git a/ruoyi-quartz/pom.xml b/ruoyi-quartz/pom.xml index 80279a82..acfd4d44 100644 --- a/ruoyi-quartz/pom.xml +++ b/ruoyi-quartz/pom.xml @@ -5,7 +5,7 @@ ruoyi com.ruoyi - 3.8.7 + 3.8.8 4.0.0 diff --git a/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobLogMapper.xml b/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobLogMapper.xml index 05a19084..5ed72647 100644 --- a/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobLogMapper.xml +++ b/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobLogMapper.xml @@ -36,10 +36,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" AND invoke_target like concat('%', #{invokeTarget}, '%') - and date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d') + and date_format(create_time,'%Y%m%d') >= date_format(#{params.beginTime},'%Y%m%d') - and date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d') + and date_format(create_time,'%Y%m%d') <= date_format(#{params.endTime},'%Y%m%d') order by create_time desc diff --git a/ruoyi-system/pom.xml b/ruoyi-system/pom.xml index d746b24e..d2099327 100644 --- a/ruoyi-system/pom.xml +++ b/ruoyi-system/pom.xml @@ -5,7 +5,7 @@ ruoyi com.ruoyi - 3.8.7 + 3.8.8 4.0.0 diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java index ba79ed5d..5df679ae 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java @@ -85,9 +85,9 @@ public interface ISysRoleService /** * 校验角色是否有数据权限 * - * @param roleId 角色id + * @param roleIds 角色id */ - public void checkRoleDataScope(Long roleId); + public void checkRoleDataScope(Long... roleIds); /** * 通过角色ID查询角色使用数量 diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java index c5f3ce6e..3ebcbd27 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java @@ -190,7 +190,7 @@ public class SysDeptServiceImpl implements ISysDeptService @Override public void checkDeptDataScope(Long deptId) { - if (!SysUser.isAdmin(SecurityUtils.getUserId())) + if (!SysUser.isAdmin(SecurityUtils.getUserId()) && StringUtils.isNotNull(deptId)) { SysDept dept = new SysDept(); dept.setDeptId(deptId); diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java index 25dd14b0..78a78307 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java @@ -188,7 +188,7 @@ public class SysMenuServiceImpl implements ISysMenuService RouterVo children = new RouterVo(); children.setPath(menu.getPath()); children.setComponent(menu.getComponent()); - children.setName(StringUtils.capitalize(menu.getPath())); + children.setName(getRouteName(menu.getRouteName(), menu.getPath())); children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath())); children.setQuery(menu.getQuery()); childrenList.add(children); @@ -203,7 +203,7 @@ public class SysMenuServiceImpl implements ISysMenuService String routerPath = innerLinkReplaceEach(menu.getPath()); children.setPath(routerPath); children.setComponent(UserConstants.INNER_LINK); - children.setName(StringUtils.capitalize(routerPath)); + children.setName(getRouteName(menu.getRouteName(), routerPath)); children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath())); childrenList.add(children); router.setChildren(childrenList); @@ -354,13 +354,25 @@ public class SysMenuServiceImpl implements ISysMenuService */ public String getRouteName(SysMenu menu) { - String routerName = StringUtils.capitalize(menu.getPath()); // 非外链并且是一级目录(类型为目录) if (isMenuFrame(menu)) { - routerName = StringUtils.EMPTY; + return StringUtils.EMPTY; } - return routerName; + return getRouteName(menu.getRouteName(), menu.getPath()); + } + + /** + * 获取路由名称,如没有配置路由名称则取路由地址 + * + * @param routerName 路由名称 + * @param path 路由地址 + * @return 路由名称(驼峰格式) + */ + public String getRouteName(String name, String path) + { + String routerName = StringUtils.isNotEmpty(name) ? name : path; + return StringUtils.capitalize(routerName); } /** diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java index 5e0a02da..81e1f8cc 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java @@ -192,19 +192,22 @@ public class SysRoleServiceImpl implements ISysRoleService /** * 校验角色是否有数据权限 * - * @param roleId 角色id + * @param roleIds 角色id */ @Override - public void checkRoleDataScope(Long roleId) + public void checkRoleDataScope(Long... roleIds) { if (!SysUser.isAdmin(SecurityUtils.getUserId())) { - SysRole role = new SysRole(); - role.setRoleId(roleId); - List roles = SpringUtils.getAopProxy(this).selectRoleList(role); - if (StringUtils.isEmpty(roles)) + for (Long roleId : roleIds) { - throw new ServiceException("没有权限访问角色数据!"); + SysRole role = new SysRole(); + role.setRoleId(roleId); + List roles = SpringUtils.getAopProxy(this).selectRoleList(role); + if (StringUtils.isEmpty(roles)) + { + throw new ServiceException("没有权限访问角色数据!"); + } } } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java index 09f3fb93..3f4dba2a 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java @@ -28,6 +28,7 @@ import com.ruoyi.system.mapper.SysUserMapper; import com.ruoyi.system.mapper.SysUserPostMapper; import com.ruoyi.system.mapper.SysUserRoleMapper; import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.system.service.ISysDeptService; import com.ruoyi.system.service.ISysUserService; /** @@ -58,6 +59,9 @@ public class SysUserServiceImpl implements ISysUserService @Autowired private ISysConfigService configService; + @Autowired + private ISysDeptService deptService; + @Autowired protected Validator validator; @@ -489,7 +493,6 @@ public class SysUserServiceImpl implements ISysUserService int failureNum = 0; StringBuilder successMsg = new StringBuilder(); StringBuilder failureMsg = new StringBuilder(); - String password = configService.selectConfigByKey("sys.user.initPassword"); for (SysUser user : userList) { try @@ -499,6 +502,8 @@ public class SysUserServiceImpl implements ISysUserService if (StringUtils.isNull(u)) { BeanValidators.validateWithException(validator, user); + deptService.checkDeptDataScope(user.getDeptId()); + String password = configService.selectConfigByKey("sys.user.initPassword"); user.setPassword(SecurityUtils.encryptPassword(password)); user.setCreateBy(operName); userMapper.insertUser(user); @@ -510,6 +515,7 @@ public class SysUserServiceImpl implements ISysUserService BeanValidators.validateWithException(validator, user); checkUserAllowed(u); checkUserDataScope(u.getUserId()); + deptService.checkDeptDataScope(user.getDeptId()); user.setUserId(u.getUserId()); user.setUpdateBy(operName); userMapper.updateUser(user); diff --git a/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml index c2116927..f4295aa0 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml @@ -51,10 +51,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" AND config_key like concat('%', #{configKey}, '%') - and date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d') + and date_format(create_time,'%Y%m%d') >= date_format(#{params.beginTime},'%Y%m%d') - and date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d') + and date_format(create_time,'%Y%m%d') <= date_format(#{params.endTime},'%Y%m%d') diff --git a/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml index 75d80a15..c5e1da9d 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml @@ -41,7 +41,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" order by dict_sort asc - where status = '0' and dict_type = #{dictType} order by dict_sort asc diff --git a/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml index 2b44b2ee..554db544 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml @@ -33,10 +33,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" AND dict_type like concat('%', #{dictType}, '%') - and date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d') + and date_format(create_time,'%Y%m%d') >= date_format(#{params.beginTime},'%Y%m%d') - and date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d') + and date_format(create_time,'%Y%m%d') <= date_format(#{params.endTime},'%Y%m%d') diff --git a/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml index e90f6baf..e6be3aef 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml @@ -13,6 +13,7 @@ + @@ -28,7 +29,7 @@ - select menu_id, menu_name, parent_id, order_num, path, component, `query`, is_frame, is_cache, menu_type, visible, status, ifnull(perms,'') as perms, icon, create_time + select menu_id, menu_name, parent_id, order_num, path, component, `query`, route_name, is_frame, is_cache, menu_type, visible, status, ifnull(perms,'') as perms, icon, create_time from sys_menu @@ -49,13 +50,13 @@