refactor: 配置刷新优化和加密功能移除

- refactor(springboot2): 简化配置刷新逻辑,移除 EnvironmentChangeEvent 包装
  - ConfigChangeHanlder 直接接收 Map<String,String> 参数
  - 移除 EnvironmentChangeEvent 事件类
  - 优化 token 参数构建和敏感信息脱敏处理
- refactor(server): 移除配置加密功能
  - 删除 CryptoConfig 结构体和相关配置项
  - 移除 application-dev.properties 中的加密配置
  - 项目不再使用配置加密功能
- docs: 更新 README 和模块文档
- fix: 修复 Node.js 客户端配置缓存
This commit is contained in:
2026-03-25 21:00:59 +08:00
parent c2e599097e
commit 751a0f770f
6 changed files with 448 additions and 571 deletions
-1
View File
@@ -64,7 +64,6 @@ database.user=root
database.password=your_password
jwt.secret=your-jwt-secret
jwt.expireHours=24
crypto.secretKey=your-crypto-key
```
启动服务(首次启动自动建表并初始化默认数据):
@@ -1,5 +1,4 @@
{
"crypto.secretKey": "k9#mP2$vL7qR4xN8",
"database.host": "127.0.0.1",
"database.name": "kenaito_conf",
"database.password": "1234567",
@@ -4,5 +4,5 @@ import java.util.Map;
public interface ConfigChangeHanlder {
public void onConfigChanged(Map<String, Object> changedConfigs);
void onConfigChanged(Map<String, Object> changedConfigs);
}
@@ -7,12 +7,6 @@ import java.security.MessageDigest;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
@@ -141,67 +135,6 @@ public class ConfigcenterContext {
return null;
}
// ==================== Crypto Utils (replaces mendmix AES/DES/Base64/DigestUtils) ====================
public static String md5(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
sb.append(String.format("%02x", b));
}
return sb.toString();
} catch (Exception e) {
throw new RuntimeException("MD5 error", e);
}
}
public static String md5Short(String input) {
if (input == null || input.isEmpty()) return "";
return md5(input).substring(0, 16);
}
private static String desEncrypt(String key, String data) {
try {
SecretKey secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "DES");
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encrypted);
} catch (Exception e) {
throw new RuntimeException("DES encrypt error", e);
}
}
private static byte[] aesDecrypt(byte[] data, byte[] key) throws Exception {
SecretKey secretKey = new SecretKeySpec(key, "AES");
// Use CBC mode with zero IV to match Go server
IvParameterSpec iv = new IvParameterSpec(new byte[16]);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
byte[] decrypted = cipher.doFinal(data);
// Remove PKCS7 padding
int padding = decrypted[decrypted.length - 1] & 0xFF;
if (padding > 0 && padding <= 16) {
byte[] result = new byte[decrypted.length - padding];
System.arraycopy(decrypted, 0, result, 0, result.length);
return result;
}
return decrypted;
}
private static String decryptWithAES(String key, String data) {
try {
String secretKey = md5(key).substring(16);
byte[] encrypted = Base64.getDecoder().decode(data.getBytes(StandardCharsets.UTF_8));
byte[] decrypted = aesDecrypt(encrypted, secretKey.getBytes(StandardCharsets.UTF_8));
return new String(decrypted, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new RuntimeException(String.format("解密错误:%s", data));
}
}
// ==================== HTTP Utils (replaces mendmix HttpUtils) ====================
public static String httpGet(String url) throws Exception {
@@ -242,8 +175,7 @@ public class ConfigcenterContext {
syncIntervalSeconds = getInt("kenaito.config.sync-interval-seconds", 60);
token = getProperty("kenaito.config.token");
logger.info("\n=====Configcenter config=====\nappName:{}\nenv:{}\nversion:{}\nremoteEnabled:{}\napiBaseUrls:{}\n=====Configcenter config=====",
app, env, version, isRemoteEnabled(), JSON.toJSONString(apiBaseUrls));
logger.info("\n=====Configcenter config=====\nappName:{}\nenv:{}\nversion:{}\nremoteEnabled:{}\napiBaseUrls:{}\n=====Configcenter config=====", app, env, version, isRemoteEnabled(), JSON.toJSONString(apiBaseUrls));
}
public boolean isRemoteEnabled() {
@@ -271,15 +203,29 @@ public class ConfigcenterContext {
}
}
public String getApp() { return app; }
public String getEnv() { return env; }
public String getVersion() { return version; }
public boolean isIgnoreGlobal() { return ignoreGlobal; }
public String getSecret() { return secret; }
public boolean isRemoteFirst() { return remoteFirst; }
public int getSyncIntervalSeconds() { return syncIntervalSeconds; }
public String getNodeId() { return nodeId; }
public boolean isSpringboot() { return isSpringboot; }
public String getApp() {
return app;
}
public String getEnv() {
return env;
}
public String getVersion() {
return version;
}
public boolean isIgnoreGlobal() {
return ignoreGlobal;
}
public boolean isRemoteFirst() {
return remoteFirst;
}
public int getSyncIntervalSeconds() {
return syncIntervalSeconds;
}
public void mergeRemoteProperties(Properties properties) {
if (!remoteEnabled) return;
@@ -343,7 +289,7 @@ public class ConfigcenterContext {
Set<String> keys = map.keySet();
for (String key : keys) {
Object value = decodeEncryptIfRequire(map.get(key));
Object value = map.get(key);
properties.put(key, value);
}
@@ -412,7 +358,7 @@ public class ConfigcenterContext {
Set<String> keySet = updateConfig.keySet();
for (String key : keySet) {
String oldValue = getProperty(key);
addProperty(key, decodeEncryptIfRequire(updateConfig.get(key)).toString());
addProperty(key, updateConfig.get(key).toString());
logger.info("Config [{}] Change, oldValue:{}, newValue:{}", key, oldValue, updateConfig.get(key));
}
@@ -494,8 +440,7 @@ public class ConfigcenterContext {
// 刷新
registry.refresh();
logger.info("[RefreshScope] RefreshScopeRegistry.refresh() completed, refreshed {} @Value fields and {} @ConfigurationProperties beans",
registry.getValueFieldCount(), registry.getConfigPropsCount());
logger.info("[RefreshScope] RefreshScopeRegistry.refresh() completed, refreshed {} @Value fields and {} @ConfigurationProperties beans", registry.getValueFieldCount(), registry.getConfigPropsCount());
} catch (Exception e) {
logger.warn("[RefreshScope] RefreshScopeRegistry.refresh() error, msg:{}", e.getMessage(), e);
}
@@ -504,21 +449,6 @@ public class ConfigcenterContext {
}
}
public boolean pingCcServer(String pingUrl, int retry) {
boolean result = false;
try {
result = httpGet(pingUrl) != null;
} catch (Exception e) {
// ignore
}
if (retry == 0) return false;
if (!result) {
try { Thread.sleep(1500); } catch (Exception e) { }
return pingCcServer(pingUrl, --retry);
}
return result;
}
public String buildTokenParameter(String url) {
if (token == null || token.isEmpty()) {
return url;
@@ -532,26 +462,6 @@ public class ConfigcenterContext {
}
}
private Object decodeEncryptIfRequire(Object data) {
if (data == null) return "";
if (data.toString().startsWith(CRYPT_PREFIX)) {
Validate.notBlank(secret, "config[kenaito.config.encrypt-secret] is required");
data = data.toString().replace(CRYPT_PREFIX, "");
String decryptString;
try {
decryptString = decryptWithAES(secret, data.toString());
} catch (Exception e) {
if (globalSecret != null && !globalSecret.isEmpty()) {
decryptString = decryptWithAES(globalSecret, data.toString());
} else {
throw e;
}
}
return decryptString;
}
return data;
}
private String hideSensitive(String key, String origin) {
if (StringUtils.isAnyBlank(key, origin)) return "";
boolean isSensitive = false;
@@ -1,24 +0,0 @@
package cn.odboy.confcenter;
import java.util.Set;
import org.springframework.context.ApplicationEvent;
public class EnvironmentChangeEvent extends ApplicationEvent {
private static final long serialVersionUID = 1L;
private Set<String> keys;
public EnvironmentChangeEvent(Set<String> keys) {
super(keys);
this.keys = keys;
}
/**
* @return the keys
*/
public Set<String> getKeys() {
return keys;
}
}
-7
View File
@@ -14,7 +14,6 @@ type Config struct {
Server ServerConfig
Database DatabaseConfig
JWT JWTConfig
Crypto CryptoConfig
}
type ServerConfig struct {
@@ -35,10 +34,6 @@ type JWTConfig struct {
ExpireHours int
}
type CryptoConfig struct {
SecretKey string
}
var Cfg *Config
func Load(path string) error {
@@ -77,8 +72,6 @@ func Load(path string) error {
Cfg.JWT.Secret = baseProps["jwt.secret"]
Cfg.JWT.ExpireHours = intProp(baseProps, "jwt.expireHours", 24)
Cfg.Crypto.SecretKey = baseProps["crypto.secretKey"]
if Cfg.Database.Host == "" {
return fmt.Errorf("missing required config: database.host")
}