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:
@@ -64,7 +64,6 @@ database.user=root
|
|||||||
database.password=your_password
|
database.password=your_password
|
||||||
jwt.secret=your-jwt-secret
|
jwt.secret=your-jwt-secret
|
||||||
jwt.expireHours=24
|
jwt.expireHours=24
|
||||||
crypto.secretKey=your-crypto-key
|
|
||||||
```
|
```
|
||||||
|
|
||||||
启动服务(首次启动自动建表并初始化默认数据):
|
启动服务(首次启动自动建表并初始化默认数据):
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"crypto.secretKey": "k9#mP2$vL7qR4xN8",
|
|
||||||
"database.host": "127.0.0.1",
|
"database.host": "127.0.0.1",
|
||||||
"database.name": "kenaito_conf",
|
"database.name": "kenaito_conf",
|
||||||
"database.password": "1234567",
|
"database.password": "1234567",
|
||||||
|
|||||||
+1
-1
@@ -4,5 +4,5 @@ import java.util.Map;
|
|||||||
|
|
||||||
public interface ConfigChangeHanlder {
|
public interface ConfigChangeHanlder {
|
||||||
|
|
||||||
public void onConfigChanged(Map<String, Object> changedConfigs);
|
void onConfigChanged(Map<String, Object> changedConfigs);
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-117
@@ -7,12 +7,6 @@ import java.security.MessageDigest;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
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.StringUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -141,67 +135,6 @@ public class ConfigcenterContext {
|
|||||||
return null;
|
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) ====================
|
// ==================== HTTP Utils (replaces mendmix HttpUtils) ====================
|
||||||
|
|
||||||
public static String httpGet(String url) throws Exception {
|
public static String httpGet(String url) throws Exception {
|
||||||
@@ -242,8 +175,7 @@ public class ConfigcenterContext {
|
|||||||
syncIntervalSeconds = getInt("kenaito.config.sync-interval-seconds", 60);
|
syncIntervalSeconds = getInt("kenaito.config.sync-interval-seconds", 60);
|
||||||
token = getProperty("kenaito.config.token");
|
token = getProperty("kenaito.config.token");
|
||||||
|
|
||||||
logger.info("\n=====Configcenter config=====\nappName:{}\nenv:{}\nversion:{}\nremoteEnabled:{}\napiBaseUrls:{}\n=====Configcenter config=====",
|
logger.info("\n=====Configcenter config=====\nappName:{}\nenv:{}\nversion:{}\nremoteEnabled:{}\napiBaseUrls:{}\n=====Configcenter config=====", app, env, version, isRemoteEnabled(), JSON.toJSONString(apiBaseUrls));
|
||||||
app, env, version, isRemoteEnabled(), JSON.toJSONString(apiBaseUrls));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRemoteEnabled() {
|
public boolean isRemoteEnabled() {
|
||||||
@@ -271,15 +203,29 @@ public class ConfigcenterContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getApp() { return app; }
|
public String getApp() {
|
||||||
public String getEnv() { return env; }
|
return app;
|
||||||
public String getVersion() { return version; }
|
}
|
||||||
public boolean isIgnoreGlobal() { return ignoreGlobal; }
|
|
||||||
public String getSecret() { return secret; }
|
public String getEnv() {
|
||||||
public boolean isRemoteFirst() { return remoteFirst; }
|
return env;
|
||||||
public int getSyncIntervalSeconds() { return syncIntervalSeconds; }
|
}
|
||||||
public String getNodeId() { return nodeId; }
|
|
||||||
public boolean isSpringboot() { return isSpringboot; }
|
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) {
|
public void mergeRemoteProperties(Properties properties) {
|
||||||
if (!remoteEnabled) return;
|
if (!remoteEnabled) return;
|
||||||
@@ -343,7 +289,7 @@ public class ConfigcenterContext {
|
|||||||
|
|
||||||
Set<String> keys = map.keySet();
|
Set<String> keys = map.keySet();
|
||||||
for (String key : keys) {
|
for (String key : keys) {
|
||||||
Object value = decodeEncryptIfRequire(map.get(key));
|
Object value = map.get(key);
|
||||||
properties.put(key, value);
|
properties.put(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,7 +358,7 @@ public class ConfigcenterContext {
|
|||||||
Set<String> keySet = updateConfig.keySet();
|
Set<String> keySet = updateConfig.keySet();
|
||||||
for (String key : keySet) {
|
for (String key : keySet) {
|
||||||
String oldValue = getProperty(key);
|
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));
|
logger.info("Config [{}] Change, oldValue:{}, newValue:{}", key, oldValue, updateConfig.get(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -494,8 +440,7 @@ public class ConfigcenterContext {
|
|||||||
|
|
||||||
// 刷新
|
// 刷新
|
||||||
registry.refresh();
|
registry.refresh();
|
||||||
logger.info("[RefreshScope] RefreshScopeRegistry.refresh() completed, refreshed {} @Value fields and {} @ConfigurationProperties beans",
|
logger.info("[RefreshScope] RefreshScopeRegistry.refresh() completed, refreshed {} @Value fields and {} @ConfigurationProperties beans", registry.getValueFieldCount(), registry.getConfigPropsCount());
|
||||||
registry.getValueFieldCount(), registry.getConfigPropsCount());
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.warn("[RefreshScope] RefreshScopeRegistry.refresh() error, msg:{}", e.getMessage(), 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) {
|
public String buildTokenParameter(String url) {
|
||||||
if (token == null || token.isEmpty()) {
|
if (token == null || token.isEmpty()) {
|
||||||
return url;
|
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) {
|
private String hideSensitive(String key, String origin) {
|
||||||
if (StringUtils.isAnyBlank(key, origin)) return "";
|
if (StringUtils.isAnyBlank(key, origin)) return "";
|
||||||
boolean isSensitive = false;
|
boolean isSensitive = false;
|
||||||
|
|||||||
-24
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -14,7 +14,6 @@ type Config struct {
|
|||||||
Server ServerConfig
|
Server ServerConfig
|
||||||
Database DatabaseConfig
|
Database DatabaseConfig
|
||||||
JWT JWTConfig
|
JWT JWTConfig
|
||||||
Crypto CryptoConfig
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServerConfig struct {
|
type ServerConfig struct {
|
||||||
@@ -35,10 +34,6 @@ type JWTConfig struct {
|
|||||||
ExpireHours int
|
ExpireHours int
|
||||||
}
|
}
|
||||||
|
|
||||||
type CryptoConfig struct {
|
|
||||||
SecretKey string
|
|
||||||
}
|
|
||||||
|
|
||||||
var Cfg *Config
|
var Cfg *Config
|
||||||
|
|
||||||
func Load(path string) error {
|
func Load(path string) error {
|
||||||
@@ -77,8 +72,6 @@ func Load(path string) error {
|
|||||||
Cfg.JWT.Secret = baseProps["jwt.secret"]
|
Cfg.JWT.Secret = baseProps["jwt.secret"]
|
||||||
Cfg.JWT.ExpireHours = intProp(baseProps, "jwt.expireHours", 24)
|
Cfg.JWT.ExpireHours = intProp(baseProps, "jwt.expireHours", 24)
|
||||||
|
|
||||||
Cfg.Crypto.SecretKey = baseProps["crypto.secretKey"]
|
|
||||||
|
|
||||||
if Cfg.Database.Host == "" {
|
if Cfg.Database.Host == "" {
|
||||||
return fmt.Errorf("missing required config: database.host")
|
return fmt.Errorf("missing required config: database.host")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user