diff --git a/.gitee/ISSUE_TEMPLATE/bug.yml b/.gitee/ISSUE_TEMPLATE/bug.yml
new file mode 100644
index 000000000..5cb780de3
--- /dev/null
+++ b/.gitee/ISSUE_TEMPLATE/bug.yml
@@ -0,0 +1,50 @@
+name: Bug 反馈
+description: 当你中发现了一个 Bug,导致应用崩溃或抛出异常,或者有一个组件存在问题,或者某些地方看起来不对劲。
+title: "[Bug]: "
+labels: ["bug"]
+body:
+ - type: textarea
+ id: version
+ attributes:
+ label: 版本
+ description: 你当前正在使用我们软件的哪个版本(pom文件内的版本号)?
+ value: |
+ jdk版本(带上尾号): 例如 1.8.0
+ 框架版本(项目启动时输出的版本号): 例如 4.4.0
+ 其他依赖版本(你觉得有必要的):
+ validations:
+ required: true
+ - type: checkboxes
+ attributes:
+ label: 功能不好用不会用是否已经看过项目文档?
+ options:
+ - label: https://plus-doc.dromara.org
+ required: true
+ - type: checkboxes
+ attributes:
+ label: 这个问题是否已经存在?
+ options:
+ - label: 我已经搜索过现有的问题 (https://gitee.com/dromara/RuoYi-Vue-Plus/issues)
+ required: true
+ - type: textarea
+ attributes:
+ label: 希望结果
+ description: 想知道你觉得怎么样是正常或者合理的。
+ validations:
+ required: true
+ - type: markdown
+ attributes:
+ label: 如何复现
+ description: 请详细告诉我们如何复现你遇到的问题
+ value: |
+ 如涉及代码 可提供一个最小代码示例 并使用```附上它 或者截图均可 越详细越好
+ 大多数问题都是 代码编写错误问题 逻辑问题 或者用法错误等问题
+ validations:
+ required: true
+ - type: textarea
+ attributes:
+ label: 相关代码与报错信息(请勿发混乱格式)
+ description: 如果可以的话,上传任何关于 bug 的截图。
+ value: |
+ [在这里上传图片]
+
diff --git a/.gitee/ISSUE_TEMPLATE/config.yml b/.gitee/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 000000000..9450a98cf
--- /dev/null
+++ b/.gitee/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,5 @@
+blank_issues_enabled: false
+contact_links:
+ - name: RuoYi-Vue-Plus 文档中心
+ url: https://plus-doc.dromara.org
+ about: 提供 RuoYi-Vue-Plus 搭建使用指南、平台基本开发使用方式、介绍、基础知识和常见问题解答
diff --git a/.gitee/ISSUE_TEMPLATE/feature.yml b/.gitee/ISSUE_TEMPLATE/feature.yml
new file mode 100644
index 000000000..5639dbb07
--- /dev/null
+++ b/.gitee/ISSUE_TEMPLATE/feature.yml
@@ -0,0 +1,43 @@
+name: 功能建议
+description: 对本项目提出一个功能建议
+title: "[功能建议]: "
+labels: ["enhancement"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ 感谢提出功能建议,我们将仔细考虑!请持续关注该issues,在加入计划后我们会有贡献者设置为负责人,同时状态成为进行中。
+ - type: textarea
+ id: related-problem
+ attributes:
+ label: 你的功能建议是否和某个问题相关?
+ description: 清晰并简洁地描述问题是什么,例如,当我...时,我总是感到困扰。
+ validations:
+ required: false
+ - type: textarea
+ id: desired-solution
+ attributes:
+ label: 你希望看到什么解决方案?
+ description: 清晰并简洁地描述你希望发生的事情。
+ validations:
+ required: true
+ - type: textarea
+ id: alternatives
+ attributes:
+ label: 你考虑过哪些替代方案?
+ description: 清晰并简洁地描述你考虑过的任何替代解决方案或功能。
+ validations:
+ required: false
+ - type: textarea
+ id: additional-context
+ attributes:
+ label: 你有其他上下文或截图吗?
+ description: 在此处添加有关功能请求的任何其他上下文或截图。
+ validations:
+ required: false
+ - type: checkboxes
+ attributes:
+ label: 意向参与贡献
+ options:
+ - label: 我有意向参与具体功能的开发实现并将代码贡献回到上游社区
+ required: false
\ No newline at end of file
diff --git a/.run/ruoyi-monitor-admin.run.xml b/.run/ruoyi-monitor-admin.run.xml
index 99818dcca..57a0e1709 100644
--- a/.run/ruoyi-monitor-admin.run.xml
+++ b/.run/ruoyi-monitor-admin.run.xml
@@ -2,7 +2,7 @@
-
+
diff --git a/.run/ruoyi-server.run.xml b/.run/ruoyi-server.run.xml
index 6c3cd5e26..c51b9d5fd 100644
--- a/.run/ruoyi-server.run.xml
+++ b/.run/ruoyi-server.run.xml
@@ -2,7 +2,7 @@
-
+
diff --git a/.run/ruoyi-xxl-job-admin.run.xml b/.run/ruoyi-xxl-job-admin.run.xml
index 98939edbd..a9657b03d 100644
--- a/.run/ruoyi-xxl-job-admin.run.xml
+++ b/.run/ruoyi-xxl-job-admin.run.xml
@@ -2,7 +2,7 @@
-
+
diff --git a/README.md b/README.md
index 97b538f8c..098497984 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,7 @@
- - -
+
## 平台简介
[](https://gitee.com/dromara/RuoYi-Vue-Plus)
@@ -9,7 +10,7 @@
[](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/master/LICENSE)
[](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
-[](https://gitee.com/dromara/RuoYi-Vue-Plus)
+[](https://gitee.com/dromara/RuoYi-Vue-Plus)
[]()
[]()
[]()
@@ -162,4 +163,3 @@
|  |  |
-
diff --git a/pom.xml b/pom.xml
index 57d6e34f5..bac3280b1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,15 +6,15 @@
com.ruoyi
ruoyi-vue-plus
- 4.6.0
+ 4.7.0
RuoYi-Vue-Plus
https://gitee.com/dromara/RuoYi-Vue-Plus
RuoYi-Vue-Plus后台管理系统
- 4.6.0
- 2.7.9
+ 4.7.0
+ 2.7.11
UTF-8
UTF-8
1.8
@@ -27,16 +27,18 @@
1.34.0
3.5.3.1
3.9.1
- 5.8.15
+ 5.8.18
4.10.0
2.7.10
- 3.20.0
+ 3.20.1
2.2.3
3.5.2
2.14.2
- 2.3.1
+ 2.4.0
1.18.26
1.72
+
+ 2.7.0
1.4
@@ -265,6 +267,13 @@
${alibaba-ttl.version}
+
+
+ org.lionsoul
+ ip2region
+ ${ip2region.version}
+
+
org.yaml
diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml
index 72accf137..d1fa747de 100644
--- a/ruoyi-admin/pom.xml
+++ b/ruoyi-admin/pom.xml
@@ -5,7 +5,7 @@
ruoyi-vue-plus
com.ruoyi
- 4.6.0
+ 4.7.0
4.0.0
jar
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java
index 554cc33f2..83abdb363 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java
@@ -10,11 +10,13 @@ import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.enums.CaptchaType;
import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.email.MailUtils;
import com.ruoyi.common.utils.redis.RedisUtils;
import com.ruoyi.common.utils.reflect.ReflectUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.config.properties.CaptchaProperties;
import com.ruoyi.rsaencrypt.annotation.RsaSecurityParameter;
+import com.ruoyi.framework.config.properties.MailProperties;
import com.ruoyi.sms.config.properties.SmsProperties;
import com.ruoyi.sms.core.SmsTemplate;
import com.ruoyi.sms.entity.SmsResult;
@@ -48,6 +50,7 @@ public class CaptchaController {
private final CaptchaProperties captchaProperties;
private final SmsProperties smsProperties;
private final ISysConfigService configService;
+ private final MailProperties mailProperties;
/**
* 短信验证码
@@ -55,8 +58,7 @@ public class CaptchaController {
* @param phonenumber 用户手机号
*/
@GetMapping("/captchaSms")
- public R smsCaptcha(@NotBlank(message = "{user.phonenumber.not.blank}")
- String phonenumber) {
+ public R smsCaptcha(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
if (!smsProperties.getEnabled()) {
return R.fail("当前系统没有开启短信功能!");
}
@@ -76,11 +78,32 @@ public class CaptchaController {
return R.ok();
}
+ /**
+ * 邮箱验证码
+ *
+ * @param email 邮箱
+ */
+ @GetMapping("/captchaEmail")
+ public R emailCode(@NotBlank(message = "{user.email.not.blank}") String email) {
+ if (!mailProperties.getEnabled()) {
+ return R.fail("当前系统没有开启邮箱功能!");
+ }
+ String key = CacheConstants.CAPTCHA_CODE_KEY + email;
+ String code = RandomUtil.randomNumbers(4);
+ RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
+ try {
+ MailUtils.sendText(email, "登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。");
+ } catch (Exception e) {
+ log.error("验证码短信发送异常 => {}", e.getMessage());
+ return R.fail(e.getMessage());
+ }
+ return R.ok();
+ }
+
/**
* 生成验证码
*/
@GetMapping("/captchaImage")
-// @RsaSecurityParameter
public R
+
+
+ org.lionsoul
+ ip2region
+
+
commons-fileupload
commons-fileupload
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java
index 73424f5bd..d3ae94864 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java
@@ -7,11 +7,6 @@ package com.ruoyi.common.constant;
*/
public interface CacheConstants {
- /**
- * 登录用户 redis key
- */
- String LOGIN_TOKEN_KEY = "Authorization:login:token:";
-
/**
* 在线用户 redis key
*/
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/EmailLoginBody.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/EmailLoginBody.java
new file mode 100644
index 000000000..b7bf81bdb
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/EmailLoginBody.java
@@ -0,0 +1,30 @@
+package com.ruoyi.common.core.domain.model;
+
+import lombok.Data;
+
+import javax.validation.constraints.Email;
+import javax.validation.constraints.NotBlank;
+
+/**
+ * 短信登录对象
+ *
+ * @author Lion Li
+ */
+
+@Data
+public class EmailLoginBody {
+
+ /**
+ * 邮箱
+ */
+ @NotBlank(message = "{user.email.not.blank}")
+ @Email(message = "{user.email.not.valid}")
+ private String email;
+
+ /**
+ * 邮箱code
+ */
+ @NotBlank(message = "{email.code.not.blank}")
+ private String emailCode;
+
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/SmsLoginBody.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/SmsLoginBody.java
index ce774ac7e..b12e74c14 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/SmsLoginBody.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/SmsLoginBody.java
@@ -14,13 +14,13 @@ import javax.validation.constraints.NotBlank;
public class SmsLoginBody {
/**
- * 用户名
+ * 手机号
*/
@NotBlank(message = "{user.phonenumber.not.blank}")
private String phonenumber;
/**
- * 用户密码
+ * 短信code
*/
@NotBlank(message = "{sms.code.not.blank}")
private String smsCode;
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/LoginType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/LoginType.java
index c91a4b94f..875e4762e 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/enums/LoginType.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/LoginType.java
@@ -22,6 +22,11 @@ public enum LoginType {
*/
SMS("sms.code.retry.limit.exceed", "sms.code.retry.limit.count"),
+ /**
+ * 邮箱登录
+ */
+ EMAIL("email.code.retry.limit.exceed", "email.code.retry.limit.count"),
+
/**
* 小程序登录
*/
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/excel/DefaultExcelListener.java b/ruoyi-common/src/main/java/com/ruoyi/common/excel/DefaultExcelListener.java
index 6cd6d2816..2a3fc3b1e 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/excel/DefaultExcelListener.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/excel/DefaultExcelListener.java
@@ -42,7 +42,7 @@ public class DefaultExcelListener extends AnalysisEventListener implements
private ExcelResult excelResult;
public DefaultExcelListener(boolean isValidate) {
- this.excelResult = new DefautExcelResult<>();
+ this.excelResult = new DefaultExcelResult<>();
this.isValidate = isValidate;
}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/excel/DefautExcelResult.java b/ruoyi-common/src/main/java/com/ruoyi/common/excel/DefaultExcelResult.java
similarity index 85%
rename from ruoyi-common/src/main/java/com/ruoyi/common/excel/DefautExcelResult.java
rename to ruoyi-common/src/main/java/com/ruoyi/common/excel/DefaultExcelResult.java
index c852ce65f..5ef65bf84 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/excel/DefautExcelResult.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/excel/DefaultExcelResult.java
@@ -12,7 +12,7 @@ import java.util.List;
* @author Yjoioooo
* @author Lion Li
*/
-public class DefautExcelResult implements ExcelResult {
+public class DefaultExcelResult implements ExcelResult {
/**
* 数据对象list
@@ -26,17 +26,17 @@ public class DefautExcelResult implements ExcelResult {
@Setter
private List errorList;
- public DefautExcelResult() {
+ public DefaultExcelResult() {
this.list = new ArrayList<>();
this.errorList = new ArrayList<>();
}
- public DefautExcelResult(List list, List errorList) {
+ public DefaultExcelResult(List list, List errorList) {
this.list = list;
this.errorList = errorList;
}
- public DefautExcelResult(ExcelResult excelResult) {
+ public DefaultExcelResult(ExcelResult excelResult) {
this.list = excelResult.getList();
this.errorList = excelResult.getErrorList();
}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/helper/DataBaseHelper.java b/ruoyi-common/src/main/java/com/ruoyi/common/helper/DataBaseHelper.java
index 196035629..33e0a78fe 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/helper/DataBaseHelper.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/helper/DataBaseHelper.java
@@ -66,7 +66,7 @@ public class DataBaseHelper {
// instr(',0,100,101,' , ',100,') <> 0
return "instr(','||" + var2 + "||',' , '," + var + ",') <> 0";
}
- // find_in_set(100 , '0,100,101')
- return "find_in_set(" + var + " , " + var2 + ") <> 0";
+ // find_in_set('100' , '0,100,101')
+ return "find_in_set('" + var + "' , " + var2 + ") <> 0";
}
}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/helper/DataPermissionHelper.java b/ruoyi-common/src/main/java/com/ruoyi/common/helper/DataPermissionHelper.java
index e2b1129f5..c3b8b47cd 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/helper/DataPermissionHelper.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/helper/DataPermissionHelper.java
@@ -10,6 +10,7 @@ import lombok.NoArgsConstructor;
import java.util.HashMap;
import java.util.Map;
+import java.util.function.Supplier;
/**
* 数据权限助手
@@ -61,4 +62,32 @@ public class DataPermissionHelper {
InterceptorIgnoreHelper.clearIgnoreStrategy();
}
+ /**
+ * 在忽略数据权限中执行
+ *
+ * @param handle 处理执行方法
+ */
+ public static void ignore(Runnable handle) {
+ enableIgnore();
+ try {
+ handle.run();
+ } finally {
+ disableIgnore();
+ }
+ }
+
+ /**
+ * 在忽略数据权限中执行
+ *
+ * @param handle 处理执行方法
+ */
+ public static T ignore(Supplier handle) {
+ enableIgnore();
+ try {
+ return handle.get();
+ } finally {
+ disableIgnore();
+ }
+ }
+
}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/DeptNameTranslationImpl.java b/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/DeptNameTranslationImpl.java
index 69196171b..ada3c253a 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/DeptNameTranslationImpl.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/DeptNameTranslationImpl.java
@@ -18,7 +18,8 @@ import org.springframework.stereotype.Component;
public class DeptNameTranslationImpl implements TranslationInterface {
private final DeptService deptService;
-
+
+ @Override
public String translation(Object key, String other) {
return deptService.selectDeptNameByIds(key.toString());
}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/DictTypeTranslationImpl.java b/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/DictTypeTranslationImpl.java
index 210312c82..1e97b66ce 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/DictTypeTranslationImpl.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/DictTypeTranslationImpl.java
@@ -20,6 +20,7 @@ public class DictTypeTranslationImpl implements TranslationInterface {
private final DictService dictService;
+ @Override
public String translation(Object key, String other) {
if (key instanceof String && StringUtils.isNotBlank(other)) {
return dictService.getDictLabel(other, key.toString());
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/OssUrlTranslationImpl.java b/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/OssUrlTranslationImpl.java
index 69ebd9afe..863e4d686 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/OssUrlTranslationImpl.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/OssUrlTranslationImpl.java
@@ -19,6 +19,7 @@ public class OssUrlTranslationImpl implements TranslationInterface {
private final OssService ossService;
+ @Override
public String translation(Object key, String other) {
return ossService.selectUrlByIds(key.toString());
}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/UserNameTranslationImpl.java b/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/UserNameTranslationImpl.java
index 36fd6c399..eccf1c8b5 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/UserNameTranslationImpl.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/translation/impl/UserNameTranslationImpl.java
@@ -19,6 +19,7 @@ public class UserNameTranslationImpl implements TranslationInterface {
private final UserService userService;
+ @Override
public String translation(Object key, String other) {
if (key instanceof Long) {
return userService.selectUserNameById((Long) key);
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/EncryptUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/EncryptUtils.java
new file mode 100644
index 000000000..691b6c0f4
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/EncryptUtils.java
@@ -0,0 +1,243 @@
+package com.ruoyi.common.utils;
+
+import cn.hutool.core.codec.Base64;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.crypto.SecureUtil;
+import cn.hutool.crypto.SmUtil;
+import cn.hutool.crypto.asymmetric.KeyType;
+import cn.hutool.crypto.asymmetric.RSA;
+import cn.hutool.crypto.asymmetric.SM2;
+
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 安全相关工具类
+ *
+ * @author 老马
+ */
+public class EncryptUtils {
+ /**
+ * 公钥
+ */
+ public static final String PUBLIC_KEY = "publicKey";
+ /**
+ * 私钥
+ */
+ public static final String PRIVATE_KEY = "privateKey";
+
+ /**
+ * Base64加密
+ *
+ * @param data 待加密数据
+ * @return 加密后字符串
+ */
+ public static String encryptByBase64(String data) {
+ return Base64.encode(data, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * Base64解密
+ *
+ * @param data 待解密数据
+ * @return 解密后字符串
+ */
+ public static String decryptByBase64(String data) {
+ return Base64.decodeStr(data, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * AES加密
+ *
+ * @param data 待解密数据
+ * @param password 秘钥字符串
+ * @return 加密后字符串, 采用Base64编码
+ */
+ public static String encryptByAes(String data, String password) {
+ if (StrUtil.isBlank(password)) {
+ throw new IllegalArgumentException("AES需要传入秘钥信息");
+ }
+ // aes算法的秘钥要求是16位、24位、32位
+ int[] array = {16, 24, 32};
+ if (!ArrayUtil.contains(array, password.length())) {
+ throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位");
+ }
+ return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * AES解密
+ *
+ * @param data 待解密数据
+ * @param password 秘钥字符串
+ * @return 解密后字符串
+ */
+ public static String decryptByAes(String data, String password) {
+ if (StrUtil.isBlank(password)) {
+ throw new IllegalArgumentException("AES需要传入秘钥信息");
+ }
+ // aes算法的秘钥要求是16位、24位、32位
+ int[] array = {16, 24, 32};
+ if (!ArrayUtil.contains(array, password.length())) {
+ throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位");
+ }
+ return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * sm4加密
+ *
+ * @param data 待加密数据
+ * @param password 秘钥字符串
+ * @return 加密后字符串, 采用Base64编码
+ */
+ public static String encryptBySm4(String data, String password) {
+ if (StrUtil.isBlank(password)) {
+ throw new IllegalArgumentException("SM4需要传入秘钥信息");
+ }
+ // sm4算法的秘钥要求是16位长度
+ int sm4PasswordLength = 16;
+ if (sm4PasswordLength != password.length()) {
+ throw new IllegalArgumentException("SM4秘钥长度要求为16位");
+ }
+ return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * sm4解密
+ *
+ * @param data 待解密数据
+ * @param password 秘钥字符串
+ * @return 解密后字符串
+ */
+ public static String decryptBySm4(String data, String password) {
+ if (StrUtil.isBlank(password)) {
+ throw new IllegalArgumentException("SM4需要传入秘钥信息");
+ }
+ // sm4算法的秘钥要求是16位长度
+ int sm4PasswordLength = 16;
+ if (sm4PasswordLength != password.length()) {
+ throw new IllegalArgumentException("SM4秘钥长度要求为16位");
+ }
+ return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * 产生sm2加解密需要的公钥和私钥
+ *
+ * @return 公私钥Map
+ */
+ public static Map generateSm2Key() {
+ Map keyMap = new HashMap<>(2);
+ SM2 sm2 = SmUtil.sm2();
+ keyMap.put(PRIVATE_KEY, sm2.getPrivateKeyBase64());
+ keyMap.put(PUBLIC_KEY, sm2.getPublicKeyBase64());
+ return keyMap;
+ }
+
+ /**
+ * sm2公钥加密
+ *
+ * @param data 待加密数据
+ * @param publicKey 公钥
+ * @return 加密后字符串, 采用Base64编码
+ */
+ public static String encryptBySm2(String data, String publicKey) {
+ if (StrUtil.isBlank(publicKey)) {
+ throw new IllegalArgumentException("SM2需要传入公钥进行加密");
+ }
+ SM2 sm2 = SmUtil.sm2(null, publicKey);
+ return sm2.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey);
+ }
+
+ /**
+ * sm2私钥解密
+ *
+ * @param data 待加密数据
+ * @param privateKey 私钥
+ * @return 解密后字符串
+ */
+ public static String decryptBySm2(String data, String privateKey) {
+ if (StrUtil.isBlank(privateKey)) {
+ throw new IllegalArgumentException("SM2需要传入私钥进行解密");
+ }
+ SM2 sm2 = SmUtil.sm2(privateKey, null);
+ return sm2.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * 产生RSA加解密需要的公钥和私钥
+ *
+ * @return 公私钥Map
+ */
+ public static Map generateRsaKey() {
+ Map keyMap = new HashMap<>(2);
+ RSA rsa = SecureUtil.rsa();
+ keyMap.put(PRIVATE_KEY, rsa.getPrivateKeyBase64());
+ keyMap.put(PUBLIC_KEY, rsa.getPublicKeyBase64());
+ return keyMap;
+ }
+
+ /**
+ * rsa公钥加密
+ *
+ * @param data 待加密数据
+ * @param publicKey 公钥
+ * @return 加密后字符串, 采用Base64编码
+ */
+ public static String encryptByRsa(String data, String publicKey) {
+ if (StrUtil.isBlank(publicKey)) {
+ throw new IllegalArgumentException("RSA需要传入公钥进行加密");
+ }
+ RSA rsa = SecureUtil.rsa(null, publicKey);
+ return rsa.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey);
+ }
+
+ /**
+ * rsa私钥解密
+ *
+ * @param data 待加密数据
+ * @param privateKey 私钥
+ * @return 解密后字符串
+ */
+ public static String decryptByRsa(String data, String privateKey) {
+ if (StrUtil.isBlank(privateKey)) {
+ throw new IllegalArgumentException("RSA需要传入私钥进行解密");
+ }
+ RSA rsa = SecureUtil.rsa(privateKey, null);
+ return rsa.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * md5加密
+ *
+ * @param data 待加密数据
+ * @return 加密后字符串, 采用Hex编码
+ */
+ public static String encryptByMd5(String data) {
+ return SecureUtil.md5(data);
+ }
+
+ /**
+ * sha256加密
+ *
+ * @param data 待加密数据
+ * @return 加密后字符串, 采用Hex编码
+ */
+ public static String encryptBySha256(String data) {
+ return SecureUtil.sha256(data);
+ }
+
+ /**
+ * sm3加密
+ *
+ * @param data 待加密数据
+ * @return 加密后字符串, 采用Hex编码
+ */
+ public static String encryptBySm3(String data) {
+ return SmUtil.sm3(data);
+ }
+
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java
index 8d3515e7f..9bf203bf9 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java
@@ -1,12 +1,7 @@
package com.ruoyi.common.utils.ip;
-import cn.hutool.core.lang.Dict;
import cn.hutool.core.net.NetUtil;
import cn.hutool.http.HtmlUtil;
-import cn.hutool.http.HttpUtil;
-import com.ruoyi.common.config.RuoYiConfig;
-import com.ruoyi.common.constant.Constants;
-import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.StringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
@@ -21,40 +16,18 @@ import lombok.extern.slf4j.Slf4j;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class AddressUtils {
- // IP地址查询
- public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";
-
// 未知地址
public static final String UNKNOWN = "XX XX";
public static String getRealAddressByIP(String ip) {
- String address = UNKNOWN;
if (StringUtils.isBlank(ip)) {
- return address;
+ return UNKNOWN;
}
// 内网不查询
ip = "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip);
if (NetUtil.isInnerIP(ip)) {
return "内网IP";
}
- if (RuoYiConfig.isAddressEnabled()) {
- try {
- String rspStr = HttpUtil.createGet(IP_URL)
- .body("ip=" + ip + "&json=true", Constants.GBK)
- .execute()
- .body();
- if (StringUtils.isEmpty(rspStr)) {
- log.error("获取地理位置异常 {}", ip);
- return UNKNOWN;
- }
- Dict obj = JsonUtils.parseMap(rspStr);
- String region = obj.getStr("pro");
- String city = obj.getStr("city");
- return String.format("%s %s", region, city);
- } catch (Exception e) {
- log.error("获取地理位置异常 {}", ip);
- }
- }
- return UNKNOWN;
+ return RegionUtils.getCityInfo(ip);
}
}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/RegionUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/RegionUtils.java
new file mode 100644
index 000000000..581061a51
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/RegionUtils.java
@@ -0,0 +1,66 @@
+package com.ruoyi.common.utils.ip;
+
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.resource.ClassPathResource;
+import cn.hutool.core.util.ObjectUtil;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.file.FileUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.lionsoul.ip2region.xdb.Searcher;
+
+import java.io.File;
+
+/**
+ * 根据ip地址定位工具类,离线方式
+ * 参考地址:集成 ip2region 实现离线IP地址定位库
+ *
+ * @author lishuyan
+ */
+@Slf4j
+public class RegionUtils {
+
+ private static final Searcher SEARCHER;
+
+ static {
+ String fileName = "/ip2region.xdb";
+ File existFile = FileUtils.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + fileName);
+ if (!FileUtils.exist(existFile)) {
+ ClassPathResource fileStream = new ClassPathResource(fileName);
+ if (ObjectUtil.isEmpty(fileStream.getStream())) {
+ throw new ServiceException("RegionUtils初始化失败,原因:IP地址库数据不存在!");
+ }
+ FileUtils.writeFromStream(fileStream.getStream(), existFile);
+ }
+
+ String dbPath = existFile.getPath();
+
+ // 1、从 dbPath 加载整个 xdb 到内存。
+ byte[] cBuff;
+ try {
+ cBuff = Searcher.loadContentFromFile(dbPath);
+ } catch (Exception e) {
+ throw new ServiceException("RegionUtils初始化失败,原因:从ip2region.xdb文件加载内容失败!" + e.getMessage());
+ }
+ // 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。
+ try {
+ SEARCHER = Searcher.newWithBuffer(cBuff);
+ } catch (Exception e) {
+ throw new ServiceException("RegionUtils初始化失败,原因:" + e.getMessage());
+ }
+ }
+
+ /**
+ * 根据IP地址离线获取城市
+ */
+ public static String getCityInfo(String ip) {
+ try {
+ ip = ip.trim();
+ // 3、执行查询
+ String region = SEARCHER.search(ip);
+ return region.replace("0|", "").replace("|0", "");
+ } catch (Exception e) {
+ log.error("IP地址离线获取城市异常 {}", ip);
+ return "未知";
+ }
+ }
+}
diff --git a/ruoyi-demo/pom.xml b/ruoyi-demo/pom.xml
index fbe3475e6..81d477381 100644
--- a/ruoyi-demo/pom.xml
+++ b/ruoyi-demo/pom.xml
@@ -5,7 +5,7 @@
ruoyi-vue-plus
com.ruoyi
- 4.6.0
+ 4.7.0
4.0.0
diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisRateLimiterController.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisRateLimiterController.java
index ee2e530ba..d0401956e 100644
--- a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisRateLimiterController.java
+++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisRateLimiterController.java
@@ -49,4 +49,16 @@ public class RedisRateLimiterController {
return R.ok("操作成功", value);
}
+ /**
+ * 测试请求IP限流(key基于参数获取)
+ * 同一IP请求受影响
+ *
+ * 简单变量获取 #变量 复杂表达式 #{#变量 != 1 ? 1 : 0}
+ */
+ @RateLimiter(count = 2, time = 10, limitType = LimitType.IP, key = "#value")
+ @GetMapping("/testObj")
+ public R testObj(String value) {
+ return R.ok("操作成功", value);
+ }
+
}
diff --git a/ruoyi-extend/pom.xml b/ruoyi-extend/pom.xml
index 512368cd9..212836acd 100644
--- a/ruoyi-extend/pom.xml
+++ b/ruoyi-extend/pom.xml
@@ -5,7 +5,7 @@
ruoyi-vue-plus
com.ruoyi
- 4.6.0
+ 4.7.0
4.0.0
ruoyi-extend
diff --git a/ruoyi-extend/ruoyi-monitor-admin/pom.xml b/ruoyi-extend/ruoyi-monitor-admin/pom.xml
index 8cb17a887..89cf455f2 100644
--- a/ruoyi-extend/ruoyi-monitor-admin/pom.xml
+++ b/ruoyi-extend/ruoyi-monitor-admin/pom.xml
@@ -5,7 +5,7 @@
ruoyi-extend
com.ruoyi
- 4.6.0
+ 4.7.0
4.0.0
jar
diff --git a/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml b/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml
index 22ef1abfc..1b729ef1b 100644
--- a/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml
+++ b/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml
@@ -6,6 +6,9 @@ spring:
profiles:
active: @profiles.active@
+logging:
+ config: classpath:logback-plus.xml
+
--- # 监控中心服务端配置
spring:
security:
diff --git a/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/logback.xml b/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/logback-plus.xml
similarity index 100%
rename from ruoyi-extend/ruoyi-monitor-admin/src/main/resources/logback.xml
rename to ruoyi-extend/ruoyi-monitor-admin/src/main/resources/logback-plus.xml
diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/pom.xml b/ruoyi-extend/ruoyi-xxl-job-admin/pom.xml
index 8e4f135db..56650a2fc 100644
--- a/ruoyi-extend/ruoyi-xxl-job-admin/pom.xml
+++ b/ruoyi-extend/ruoyi-xxl-job-admin/pom.xml
@@ -4,7 +4,7 @@
ruoyi-extend
com.ruoyi
- 4.6.0
+ 4.7.0
ruoyi-xxl-job-admin
jar
diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobGroupController.java b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobGroupController.java
index 82ec2dee4..5cbf87287 100644
--- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobGroupController.java
+++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobGroupController.java
@@ -1,5 +1,6 @@
package com.xxl.job.admin.controller;
+import com.xxl.job.admin.controller.annotation.PermissionLimit;
import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobRegistry;
import com.xxl.job.admin.core.util.I18nUtil;
@@ -35,12 +36,14 @@ public class JobGroupController {
private XxlJobRegistryDao xxlJobRegistryDao;
@RequestMapping
+ @PermissionLimit(adminuser = true)
public String index(Model model) {
return "jobgroup/jobgroup.index";
}
@RequestMapping("/pageList")
@ResponseBody
+ @PermissionLimit(adminuser = true)
public Map pageList(HttpServletRequest request,
@RequestParam(required = false, defaultValue = "0") int start,
@RequestParam(required = false, defaultValue = "10") int length,
@@ -60,6 +63,7 @@ public class JobGroupController {
@RequestMapping("/save")
@ResponseBody
+ @PermissionLimit(adminuser = true)
public ReturnT save(XxlJobGroup xxlJobGroup) {
// valid
@@ -103,6 +107,7 @@ public class JobGroupController {
@RequestMapping("/update")
@ResponseBody
+ @PermissionLimit(adminuser = true)
public ReturnT update(XxlJobGroup xxlJobGroup) {
// valid
if (xxlJobGroup.getAppname() == null || xxlJobGroup.getAppname().trim().length() == 0) {
@@ -171,6 +176,7 @@ public class JobGroupController {
@RequestMapping("/remove")
@ResponseBody
+ @PermissionLimit(adminuser = true)
public ReturnT remove(int id) {
// valid
@@ -190,6 +196,7 @@ public class JobGroupController {
@RequestMapping("/loadById")
@ResponseBody
+ @PermissionLimit(adminuser = true)
public ReturnT loadById(int id) {
XxlJobGroup jobGroup = xxlJobGroupDao.load(id);
return jobGroup != null ? new ReturnT(jobGroup) : new ReturnT(ReturnT.FAIL_CODE, null);
diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java
index 3369edb8f..e741c174b 100644
--- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java
+++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java
@@ -130,22 +130,26 @@ public class JobLogController {
model.addAttribute("triggerCode", jobLog.getTriggerCode());
model.addAttribute("handleCode", jobLog.getHandleCode());
- model.addAttribute("executorAddress", jobLog.getExecutorAddress());
- model.addAttribute("triggerTime", jobLog.getTriggerTime().getTime());
model.addAttribute("logId", jobLog.getId());
return "joblog/joblog.detail";
}
@RequestMapping("/logDetailCat")
@ResponseBody
- public ReturnT logDetailCat(String executorAddress, long triggerTime, long logId, int fromLineNum) {
+ public ReturnT logDetailCat(long logId, int fromLineNum) {
try {
- ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(executorAddress);
- ReturnT logResult = executorBiz.log(new LogParam(triggerTime, logId, fromLineNum));
+ // valid
+ XxlJobLog jobLog = xxlJobLogDao.load(logId); // todo, need to improve performance
+ if (jobLog == null) {
+ return new ReturnT(ReturnT.FAIL_CODE, I18nUtil.getString("joblog_logid_unvalid"));
+ }
+
+ // log cat
+ ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(jobLog.getExecutorAddress());
+ ReturnT logResult = executorBiz.log(new LogParam(jobLog.getTriggerTime().getTime(), logId, fromLineNum));
// is end
if (logResult.getContent() != null && logResult.getContent().getFromLineNum() > logResult.getContent().getToLineNum()) {
- XxlJobLog jobLog = xxlJobLogDao.load(logId);
if (jobLog.getHandleCode() > 0) {
logResult.getContent().setEnd(true);
}
diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application.yml b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application.yml
index d4fda939a..6d0829217 100644
--- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application.yml
+++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application.yml
@@ -16,6 +16,9 @@ spring:
resources:
static-locations: classpath:/static/
+logging:
+ config: classpath:logback-plus.xml
+
--- # mybatis 配置
mybatis:
mapper-locations: classpath:/mybatis-mapper/*Mapper.xml
diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_en.properties b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_en.properties
index 8b3c8014b..881bb8d68 100644
--- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_en.properties
+++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_en.properties
@@ -1,6 +1,6 @@
admin_name=Scheduling Center
admin_name_full=Distributed Task Scheduling Platform XXL-JOB
-admin_version=2.3.1
+admin_version=2.4.0
admin_i18n=en
## system
diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_zh_CN.properties b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_zh_CN.properties
index b3860e49d..5be17ffe1 100644
--- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_zh_CN.properties
+++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_zh_CN.properties
@@ -1,6 +1,6 @@
admin_name=任务调度中心
admin_name_full=分布式任务调度平台XXL-JOB
-admin_version=2.3.1
+admin_version=2.4.0
admin_i18n=
## system
diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_zh_TC.properties b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_zh_TC.properties
index ca069b343..3250f1a9c 100644
--- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_zh_TC.properties
+++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/i18n/message_zh_TC.properties
@@ -1,6 +1,6 @@
admin_name=任務調度中心
admin_name_full=分布式任務調度平臺XXL-JOB
-admin_version=2.3.1
+admin_version=2.4.0
admin_i18n=
## system
diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/logback.xml b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/logback-plus.xml
similarity index 100%
rename from ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/logback.xml
rename to ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/logback-plus.xml
diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/joblog.detail.1.js b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/joblog.detail.1.js
index ddefd4663..0638eee27 100644
--- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/joblog.detail.1.js
+++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/static/js/joblog.detail.1.js
@@ -25,8 +25,6 @@ $(function() {
async: false, // sync, make log ordered
url : base_url + '/joblog/logDetailCat',
data : {
- "executorAddress":executorAddress,
- "triggerTime":triggerTime,
"logId":logId,
"fromLineNum":fromLineNum
},
diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/joblog/joblog.detail.ftl b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/joblog/joblog.detail.ftl
index 3881cfa6a..0ea69c763 100644
--- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/joblog/joblog.detail.ftl
+++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/templates/joblog/joblog.detail.ftl
@@ -62,11 +62,9 @@
// 参数
var triggerCode = '${triggerCode}';
var handleCode = '${handleCode}';
- var executorAddress = '${executorAddress!}';
- var triggerTime = '${triggerTime?c}';
var logId = '${logId}';