diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/agentcenter/AgentCenterController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/agentcenter/AgentCenterController.java index b2034be..307b0cb 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/agentcenter/AgentCenterController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/agentcenter/AgentCenterController.java @@ -36,7 +36,7 @@ public class AgentCenterController extends BaseController NSSDKServer server; NSReportObject recv; - NSSDK.InitServer(RuoYiConfig.Secret.privateKey); + NSSDK.InitServer(RuoYiConfig.AgentCenter.privateKey); server = NSSDK.InstanceServer(); recv = server.Recv(request, NSContractionMessage.class); @@ -49,7 +49,7 @@ public class AgentCenterController extends BaseController NSSDKServer server; Session session; - NSSDK.InitServer(RuoYiConfig.Secret.privateKey); + NSSDK.InitServer(RuoYiConfig.AgentCenter.privateKey); server = NSSDK.InstanceServer(); try { diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index 9b6bd2c..d40e289 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -130,8 +130,16 @@ xss: # 匹配链接 urlPatterns: /system/*,/monitor/*,/tool/* - +# 传输加密(公钥,私钥) secret: + # 是否启用 + enabled: true + # 是否兼容不加密明文 + compat: false + publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ== + privateKey: MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKNPuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gAkM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWowcSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99EcvDQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthhYhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3UP8iWi1Qw0Y= + +agentcenter: disabled: false # UNUSED # 客户端公钥 publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ== diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java b/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java index af36816..c19bb6f 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java @@ -1,5 +1,6 @@ package com.ruoyi.common.config; +import com.ruoyi.common.utils.SecretUtils; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @@ -137,14 +138,42 @@ public class RuoYiConfig @Component - @ConfigurationProperties(prefix = "secret") - public static class Secret { + @ConfigurationProperties(prefix = "agentcenter") + public static class AgentCenter { public static Boolean disabled; public static String publicKey; public static String privateKey; public void setDisabled(Boolean disabled) { - Secret.disabled = disabled; + AgentCenter.disabled = disabled; + } + + public void setPublicKey(String publicKey) { + AgentCenter.publicKey = publicKey; + } + + public void setPrivateKey(String privateKey) { + AgentCenter.privateKey = privateKey; + } + + public static boolean isDisabled() { + return null != AgentCenter.disabled /*默认加密*/ && AgentCenter.disabled; + } + } + @Component + @ConfigurationProperties(prefix = "secret") + public static class Secret { + public static Boolean enabled; + public static String publicKey; + public static String privateKey; + public static Boolean compat; + + public void setCompat(Boolean compat) { + Secret.compat = compat; + } + + public void setEnabled(Boolean enabled) { + Secret.enabled = enabled; } public void setPublicKey(String publicKey) { @@ -156,7 +185,21 @@ public class RuoYiConfig } public static boolean isDisabled() { - return null != Secret.disabled /*默认加密*/ && Secret.disabled; + return null == Secret.enabled /*默认不加密*/ || !Secret.enabled; + } + + public static boolean isCompat() { + return null != Secret.compat /*默认不兼容*/ && Secret.compat; + } + + // 私钥解密 + public static String decrypt(String inStr) { + try { + return SecretUtils.decrypt(inStr, Secret.privateKey, StandardCharsets.UTF_8.name()); + } catch (Exception e) { + //e.printStackTrace(); + throw new RuntimeException(e); + } } } } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/ASSERT.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/ASSERT.java new file mode 100644 index 0000000..2766987 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/ASSERT.java @@ -0,0 +1,146 @@ +package com.ruoyi.common.exception; + +import java.util.Map; +import java.util.Objects; + +/** + * 断言异常 + * + * @author zhao + */ +public final class ASSERT extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public final String message; + + public ASSERT(String message) + { + this.message = message; + } + + @Override + public String getMessage() + { + return message; + } + + public static String Output(ASSERT e) + { + StringBuilder sb = new StringBuilder(); + sb.append(e.message); + StackTraceElement[] stackTrace = e.getStackTrace(); + if(null != stackTrace) + { + for(int i = 0; i < stackTrace.length; i++) + { + StackTraceElement st = stackTrace[i]; + if(st.getClassName().startsWith("com.ruoyi.")) + { + sb.append("\n").append("\t").append(i).append(": ").append(st.toString()); + } + } + } + return sb.toString(); + } + + public static void THROW(String message, Object...args) + { + throw new ASSERT(null != message ? String.format(message, args) : "断言失败"); + } + + public static void EXP(boolean expression, String errorMsgTemplate, Object... params) throws ASSERT { + if(!expression) + THROW(errorMsgTemplate, params); + } + public static void EXP(boolean expression) throws ASSERT { + EXP(expression, "断言表达式为假"); + } + + public static void TRUE(boolean expression, String errorMsgTemplate, Object... params) throws ASSERT { + if(!expression) + THROW(errorMsgTemplate, params); + } + + public static void TRUE(boolean expression) throws ASSERT { + TRUE(expression, "[断言失败] - 表达式必须为true"); + } + + public static void FALSE(boolean expression, String errorMsgTemplate, Object... params) throws ASSERT { + if(expression) + THROW(errorMsgTemplate, params); + } + + public static void FALSE(boolean expression) throws ASSERT { + FALSE(expression, "[断言失败] - 表达式必须false"); + } + + public static void NULL(Object object, String errorMsgTemplate, Object... params) throws ASSERT { + if(Objects.nonNull(object)) + THROW(errorMsgTemplate, params); + } + + public static void NULL(Object object) throws ASSERT { + NULL(object, "[断言失败] - 对象必须为null"); + } + + public static T NONNULL(T object, String errorMsgTemplate, Object... params) throws ASSERT { + if(Objects.isNull(object)) + THROW(errorMsgTemplate, params); + return object; + } + + public static T NONNULL(T object) throws ASSERT { + return NONNULL(object, "[断言失败] - 对象必须非null"); + } + + public static String NOTEMPTY(String text, String errorMsgTemplate, Object... params) throws ASSERT { + if(null == text || text.isEmpty() || text.trim().isEmpty()) + THROW(errorMsgTemplate, params); + return text; + } + + public static String NOTEMPTY(String text) throws ASSERT { + return NOTEMPTY(text, "[断言失败] - 字符串必须有非空字符数"); + } + + public static String EMPTY(String text, String errorMsgTemplate, Object... params) throws ASSERT { + if(null != text && !text.isEmpty() && !text.trim().isEmpty()) + THROW(errorMsgTemplate, params); + return text; + } + + public static String EMPTY(String text) throws ASSERT { + return EMPTY(text, "[断言失败] - 字符串必须不能包含非空字符"); + } + + public static T[] NOTEMPTY(T[] array, String errorMsgTemplate, Object... params) throws ASSERT { + if(null == array || array.length == 0) + THROW(errorMsgTemplate, params); + return array; + } + + public static T[] NOTEMPTY(T[] array) throws ASSERT { + return NOTEMPTY(array, "[断言失败] - 数组必须非空"); + } + + public static Iterable NOTEMPTY(Iterable collection, String errorMsgTemplate, Object... params) throws ASSERT { + if(null == collection || !collection.iterator().hasNext() ) + THROW(errorMsgTemplate, params); + return collection; + } + + public static Iterable NOTEMPTY(Iterable collection) throws ASSERT { + return NOTEMPTY(collection, "[断言失败] - 集合容器必须非空"); + } + + public static Map NOTEMPTY(Map map, String errorMsgTemplate, Object... params) throws ASSERT { + if(null == map || map.isEmpty() ) + THROW(errorMsgTemplate, params); + return map; + } + + public static Map NOTEMPTY(Map map) throws ASSERT { + return NOTEMPTY(map, "[断言失败] - 关联容器必须非空"); + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/SecretUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/SecretUtils.java new file mode 100644 index 0000000..4f3524e --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/SecretUtils.java @@ -0,0 +1,30 @@ +package com.ruoyi.common.utils; + +import org.apache.commons.codec.binary.Base64; +import javax.crypto.Cipher; +import java.security.*; +import java.security.interfaces.RSAPrivateKey; +import java.security.spec.PKCS8EncodedKeySpec; + +/** + * 密钥工具类 + * + * @author nsgk + */ +public class SecretUtils { + + public static String decrypt(String str, String privateKey, String...codec) throws Exception { + //64位解码加密后的字符串 + byte[] inputByte = Base64.decodeBase64(str); + //base64编码的私钥 + byte[] decoded = Base64.decodeBase64(privateKey); + RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded)); + //RSA解密 + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, priKey); + //这里直接new String() , 不用传输,为了直接查看解密后的明文(base64加密后看不懂) + String outStr = codec.length > 0 ? new String(cipher.doFinal(inputByte), codec[0]) : new String(cipher.doFinal(inputByte)); +// byte[] decode = Base64.getDecoder().decode(inputByte); base64编码后的明文 + return outStr; + } +} 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 51dd8c5..71d0da5 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 @@ -1,6 +1,8 @@ package com.ruoyi.framework.web.exception; import javax.servlet.http.HttpServletRequest; + +import com.ruoyi.common.exception.ASSERT; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.access.AccessDeniedException; @@ -111,4 +113,15 @@ public class GlobalExceptionHandler { return AjaxResult.error("演示模式,不允许操作"); } + + /** + * 拦截断言异常: 不输出异常栈 + */ + @ExceptionHandler(ASSERT.class) + public AjaxResult handleAssertFail(ASSERT e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生未知异常: {}.", requestURI, ASSERT.Output(e)); + return AjaxResult.error(e.getMessage()); + } } 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 67267b7..7213cb8 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 @@ -1,6 +1,9 @@ package com.ruoyi.framework.web.service; import javax.annotation.Resource; + +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.exception.ASSERT; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; @@ -52,6 +55,23 @@ public class SysLoginService @Autowired private ISysConfigService configService; + private String D(String inStr) + { + if(RuoYiConfig.Secret.isDisabled()) + return inStr; + try + { + return RuoYiConfig.Secret.decrypt(inStr); + } + catch(Exception e) + { + if(RuoYiConfig.Secret.isCompat()) + return inStr; + else + throw new ASSERT("无效登录凭据"); + } + } + /** * 登录验证 * @@ -63,6 +83,7 @@ public class SysLoginService */ public String login(String username, String password, String code, String uuid) { + username = D(username); password = D(password); // 验证码校验 validateCaptcha(username, code, uuid); // 登录前置校验