@@ -36,7 +36,7 @@ public class AgentCenterController extends BaseController | |||
NSSDKServer server; | |||
NSReportObject<NSContractionMessage> 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 | |||
{ | |||
@@ -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== | |||
@@ -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); | |||
} | |||
} | |||
} | |||
} |
@@ -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> T NONNULL(T object, String errorMsgTemplate, Object... params) throws ASSERT { | |||
if(Objects.isNull(object)) | |||
THROW(errorMsgTemplate, params); | |||
return object; | |||
} | |||
public static <T> 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> T[] NOTEMPTY(T[] array, String errorMsgTemplate, Object... params) throws ASSERT { | |||
if(null == array || array.length == 0) | |||
THROW(errorMsgTemplate, params); | |||
return array; | |||
} | |||
public static <T> T[] NOTEMPTY(T[] array) throws ASSERT { | |||
return NOTEMPTY(array, "[断言失败] - 数组必须非空"); | |||
} | |||
public static <T> Iterable<T> NOTEMPTY(Iterable<T> collection, String errorMsgTemplate, Object... params) throws ASSERT { | |||
if(null == collection || !collection.iterator().hasNext() ) | |||
THROW(errorMsgTemplate, params); | |||
return collection; | |||
} | |||
public static <T> Iterable<T> NOTEMPTY(Iterable<T> collection) throws ASSERT { | |||
return NOTEMPTY(collection, "[断言失败] - 集合容器必须非空"); | |||
} | |||
public static <K, V> Map<K, V> NOTEMPTY(Map<K, V> map, String errorMsgTemplate, Object... params) throws ASSERT { | |||
if(null == map || map.isEmpty() ) | |||
THROW(errorMsgTemplate, params); | |||
return map; | |||
} | |||
public static <K, V> Map<K, V> NOTEMPTY(Map<K, V> map) throws ASSERT { | |||
return NOTEMPTY(map, "[断言失败] - 关联容器必须非空"); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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()); | |||
} | |||
} |
@@ -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); | |||
// 登录前置校验 | |||