diff --git a/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java b/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java index f1e5c32bb..d91373890 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java @@ -58,7 +58,7 @@ public class CaptchaController { * @param phoneNumber 用户手机号 * @return 操作结果 */ - @RateLimiter(key = "#phonenumber", time = 60, count = 1) + @RateLimiter(key = "#phoneNumber", time = 60, count = 1) @GetMapping("/resource/sms/code") public R smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phoneNumber) { if (!RegexValidator.isMobile(phoneNumber)) { @@ -66,7 +66,6 @@ public class CaptchaController { } String key = GlobalConstants.CAPTCHA_CODE_KEY + phoneNumber; String code = RandomUtil.randomNumbers(4); - RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); // 验证码模板id 自行处理 (查数据库或写死均可) String templateId = ""; LinkedHashMap map = new LinkedHashMap<>(1); @@ -75,8 +74,10 @@ public class CaptchaController { SmsResponse smsResponse = smsBlend.sendMessage(phoneNumber, templateId, map); if (!smsResponse.isSuccess()) { log.error("验证码短信发送异常 => {}", smsResponse); - return R.fail(smsResponse.getData().toString()); + Object data = smsResponse.getData(); + return R.fail(data == null ? "验证码短信发送失败" : data.toString()); } + RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); return R.ok(); } @@ -107,13 +108,13 @@ public class CaptchaController { public void emailCodeImpl(String email) { String key = GlobalConstants.CAPTCHA_CODE_KEY + email; String code = RandomUtil.randomNumbers(4); - RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); try { MailBuilder.of() .to(email) .subject("登录验证码") .text("您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。") .send(); + RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); } catch (Exception e) { log.error("验证码短信发送异常 => {}", e.getMessage()); throw new ServiceException(e.getMessage()); diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ValidatorUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ValidatorUtils.java index 06b8fd66e..f5f8646fe 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ValidatorUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ValidatorUtils.java @@ -26,6 +26,9 @@ public class ValidatorUtils { * @throws ConstraintViolationException 如果校验不通过,则抛出参数校验异常 */ public static void validate(T object, Class... groups) { + if (object == null) { + throw new RuntimeException("请求参数不能为空"); + } Set> validate = VALID.validate(object, groups); if (!validate.isEmpty()) { throw new ConstraintViolationException("参数校验异常", validate); diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DropDownOptions.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DropDownOptions.java index 41d953521..05b362ecf 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DropDownOptions.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DropDownOptions.java @@ -51,6 +51,9 @@ public class DropDownOptions { * 分隔符 */ private static final String DELIMITER = "_"; + private static final String OPTION_PART_REGEX = "^[A-Za-z0-9\\u4e00-\\u9fa5]+$"; + private static final String EXCEL_NAME_REGEX = "^[A-Za-z_\\u4e00-\\u9fa5][A-Za-z0-9_\\u4e00-\\u9fa5]*$"; + private static final String CELL_REFERENCE_REGEX = "^[A-Za-z]{1,3}[1-9][0-9]*$"; /** * 创建只有一级的下拉选 @@ -69,10 +72,9 @@ public class DropDownOptions { */ public static String createOptionValue(Object... vars) { StringBuilder stringBuffer = new StringBuilder(); - String regex = "^[\\S\\d\\u4e00-\\u9fa5]+$"; for (int i = 0; i < vars.length; i++) { String var = StrUtil.trimToEmpty(Convert.toStr(vars[i])); - if (!var.matches(regex)) { + if (!var.matches(OPTION_PART_REGEX)) { throw new ServiceException("选项数据不符合规则,仅允许使用中英文字符以及数字"); } stringBuffer.append(var); @@ -81,10 +83,29 @@ public class DropDownOptions { stringBuffer.append(DELIMITER); } } - if (stringBuffer.toString().matches("^\\d_*$")) { + String optionValue = stringBuffer.toString(); + validateOptionValue(optionValue); + return optionValue; + } + + /** + * 校验级联下拉选项值是否可作为 Excel 名称管理器名称。 + * + * @param optionValue 选项值 + */ + public static void validateOptionValue(String optionValue) { + if (StrUtil.isBlank(optionValue)) { + throw new ServiceException("选项数据不能为空"); + } + if (optionValue.matches("^[0-9].*")) { throw new ServiceException("禁止以数字开头"); } - return stringBuffer.toString(); + if (!optionValue.matches(EXCEL_NAME_REGEX)) { + throw new ServiceException("选项数据不符合Excel名称规则,仅允许中英文、数字或下划线,且不能以数字开头"); + } + if (optionValue.matches(CELL_REFERENCE_REGEX)) { + throw new ServiceException("选项数据不能为Excel单元格引用"); + } } /** diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java index f8dd5f864..6912386c6 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java @@ -202,6 +202,7 @@ public class ExcelDownHandler implements SheetWriteHandler { if (CollUtil.isEmpty(firstOptions)) { return; } + validateLinkedOptionNames(firstOptions); Map> secoundOptionsMap = new HashMap<>(); options.getNextOptions().forEach((k, v) -> secoundOptionsMap.put(k, new ArrayList<>(v))); @@ -313,6 +314,19 @@ public class ExcelDownHandler implements SheetWriteHandler { currentLinkedOptionsSheetIndex++; } + /** + * 校验级联下拉一级选项可作为 Excel 名称,且不能重复。 + */ + private void validateLinkedOptionNames(List firstOptions) { + Set names = new HashSet<>(); + for (String firstOption : firstOptions) { + DropDownOptions.validateOptionValue(firstOption); + if (!names.add(firstOption)) { + throw new ServiceException("级联下拉一级选项重复:" + firstOption); + } + } + } + /** *

额外表格形式的普通下拉框

* 由于下拉框可选值数量过多,为提升Excel打开效率,使用额外表格形式做下拉 diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/aspectj/RepeatSubmitAspect.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/aspectj/RepeatSubmitAspect.java index 8a5490ac4..f24e9159c 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/aspectj/RepeatSubmitAspect.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/aspectj/RepeatSubmitAspect.java @@ -24,6 +24,7 @@ import org.dromara.common.redis.utils.RedisUtils; import org.springframework.validation.BindingResult; import org.springframework.web.multipart.MultipartFile; +import java.lang.reflect.Array; import java.time.Duration; import java.util.Collection; import java.util.Map; @@ -150,16 +151,30 @@ public class RepeatSubmitAspect { public boolean isFilterObject(final Object o) { Class clazz = o.getClass(); if (clazz.isArray()) { - return MultipartFile.class.isAssignableFrom(clazz.getComponentType()); + if (MultipartFile.class.isAssignableFrom(clazz.getComponentType())) { + return true; + } + int length = Array.getLength(o); + for (int i = 0; i < length; i++) { + Object value = Array.get(o, i); + if (ObjectUtil.isNotNull(value) && isFilterObject(value)) { + return true; + } + } + return false; } else if (Collection.class.isAssignableFrom(clazz)) { Collection collection = (Collection) o; for (Object value : collection) { - return value instanceof MultipartFile; + if (ObjectUtil.isNotNull(value) && isFilterObject(value)) { + return true; + } } } else if (Map.class.isAssignableFrom(clazz)) { Map map = (Map) o; for (Object value : map.values()) { - return value instanceof MultipartFile; + if (ObjectUtil.isNotNull(value) && isFilterObject(value)) { + return true; + } } } return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/DeptExcelConverter.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/DeptExcelConverter.java index d370c3dab..e6a8b20d3 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/DeptExcelConverter.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/DeptExcelConverter.java @@ -10,6 +10,7 @@ import org.apache.fesod.sheet.metadata.GlobalConfiguration; import org.apache.fesod.sheet.metadata.data.ReadCellData; import org.apache.fesod.sheet.metadata.data.WriteCellData; import org.apache.fesod.sheet.metadata.property.ExcelContentProperty; +import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.TreeBuildUtils; @@ -93,7 +94,11 @@ public class DeptExcelConverter implements Converter { if (StringUtils.isBlank(deptName)) { return null; } - return getDeptMaps().nameToId().get(deptName); + Long deptId = getDeptMaps().nameToId().get(deptName); + if (deptId == null) { + throw new ServiceException("部门不存在:" + deptName); + } + return deptId; } /**