fix:配置加密机制

This commit is contained in:
waner 2026-05-14 14:53:35 +08:00
parent 3a9a76c100
commit e7f9c1d183
4 changed files with 55 additions and 15 deletions

View File

@ -1,7 +1,9 @@
package com.cisd.tms.common.crypto.config; package com.cisd.tms.common.crypto.config;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
@ -27,14 +29,18 @@ public class ConfigDecryptingEnvironmentPostProcessor implements EnvironmentPost
@Override @Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
Map<String, Object> decryptedValues = new LinkedHashMap<>(); Map<String, Object> decryptedValues = new LinkedHashMap<>();
Set<String> visitedPropertyNames = new HashSet<>();
for (PropertySource<?> propertySource : environment.getPropertySources()) { for (PropertySource<?> propertySource : environment.getPropertySources()) {
if (!(propertySource instanceof EnumerablePropertySource<?> enumerablePropertySource)) { if (!(propertySource instanceof EnumerablePropertySource<?> enumerablePropertySource)) {
continue; continue;
} }
for (String propertyName : enumerablePropertySource.getPropertyNames()) { for (String propertyName : enumerablePropertySource.getPropertyNames()) {
Object rawValue = enumerablePropertySource.getProperty(propertyName); if (!visitedPropertyNames.add(propertyName)) {
if (rawValue instanceof String stringValue && encryptor.isEncrypted(stringValue)) { continue;
decryptedValues.putIfAbsent(propertyName, encryptor.decryptIfNecessary(stringValue)); }
String resolvedValue = environment.getProperty(propertyName);
if (encryptor.isEncrypted(resolvedValue)) {
decryptedValues.put(propertyName, encryptor.decryptIfNecessary(resolvedValue));
} }
} }
} }

View File

@ -93,7 +93,7 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
Process process = null; Process process = null;
try { try {
ProcessBuilder processBuilder = new ProcessBuilder("/bin/zsh", "-lc", command); ProcessBuilder processBuilder = new ProcessBuilder("/bin/bash", "-lc", command);
processBuilder.redirectErrorStream(true); processBuilder.redirectErrorStream(true);
processBuilder.redirectOutput(logFile.toFile()); processBuilder.redirectOutput(logFile.toFile());
process = processBuilder.start(); process = processBuilder.start();
@ -897,7 +897,7 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
try { try {
int code = runCommand( int code = runCommand(
logFile, logFile,
Arrays.asList("/bin/zsh", "-lc", applyCommand), Arrays.asList("/bin/bash", "-lc", applyCommand),
false, false,
loadDir, loadDir,
buildStandardDbApplyEnvironment(loadDir, context) buildStandardDbApplyEnvironment(loadDir, context)
@ -1031,7 +1031,7 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
try { try {
int code = runCommand( int code = runCommand(
logFile, logFile,
Arrays.asList("/bin/zsh", "-lc", command), Arrays.asList("/bin/bash", "-lc", command),
false, false,
workingDirectory, workingDirectory,
buildStandardStartEnvironment(stepName) buildStandardStartEnvironment(stepName)
@ -1070,7 +1070,7 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
try { try {
int code = runCommand( int code = runCommand(
logFile, logFile,
Arrays.asList("/bin/zsh", "-lc", command), Arrays.asList("/bin/bash", "-lc", command),
false, false,
null, null,
buildStandardDbApplyEnvironment(null, context) buildStandardDbApplyEnvironment(null, context)
@ -1246,7 +1246,7 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
.toAbsolutePath(); .toAbsolutePath();
Path logFile = prepareLogPath(task, step); Path logFile = prepareLogPath(task, step);
try { try {
int code = runCommand(logFile, Arrays.asList("/bin/zsh", "-lc", command), false, scriptDir); int code = runCommand(logFile, Arrays.asList("/bin/bash", "-lc", command), false, scriptDir);
if (code != 0) { if (code != 0) {
return InitStepExecutionResult.failure("cae_update 执行失败", code, logFile.toString()); return InitStepExecutionResult.failure("cae_update 执行失败", code, logFile.toString());
} }
@ -1315,7 +1315,7 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
Path logFile = prepareLogPath(task, step); Path logFile = prepareLogPath(task, step);
try { try {
int code = runCommand(logFile, Arrays.asList("/bin/zsh", "-lc", command), false); int code = runCommand(logFile, Arrays.asList("/bin/bash", "-lc", command), false);
if (code != 0) { if (code != 0) {
return InitStepExecutionResult.failure("restart_tlq 执行失败", code, logFile.toString()); return InitStepExecutionResult.failure("restart_tlq 执行失败", code, logFile.toString());
} }
@ -2246,7 +2246,7 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
try { try {
int code = runCommand( int code = runCommand(
logFile, logFile,
Arrays.asList("/bin/zsh", "-lc", "pgrep -f " + quoteShellValue(pattern) + " >/dev/null"), Arrays.asList("/bin/bash", "-lc", "pgrep -f " + quoteShellValue(pattern) + " >/dev/null"),
true true
); );
if (code == 0) { if (code == 0) {
@ -2270,7 +2270,7 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
try { try {
int code = runCommand( int code = runCommand(
logFile, logFile,
Arrays.asList("/bin/zsh", "-lc", command), Arrays.asList("/bin/bash", "-lc", command),
true, true,
null, null,
buildStandardDbApplyEnvironment(null, context) buildStandardDbApplyEnvironment(null, context)
@ -2301,7 +2301,7 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
private void verifyRabbitObjectAbsent(Path logFile, String shellCommand, String message, List<String> failures) throws IOException { private void verifyRabbitObjectAbsent(Path logFile, String shellCommand, String message, List<String> failures) throws IOException {
try { try {
int code = runCommand(logFile, Arrays.asList("/bin/zsh", "-lc", shellCommand), true); int code = runCommand(logFile, Arrays.asList("/bin/bash", "-lc", shellCommand), true);
if (code == 0) { if (code == 0) {
failures.add(message); failures.add(message);
} }

View File

@ -3,7 +3,9 @@ package com.cisd.tms.common.crypto.config;
import java.util.Map; import java.util.Map;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.StandardEnvironment; import org.springframework.core.env.StandardEnvironment;
@ -25,4 +27,35 @@ class ConfigDecryptingEnvironmentPostProcessorTest {
Assertions.assertEquals("db-password", environment.getProperty("spring.datasource.password")); Assertions.assertEquals("db-password", environment.getProperty("spring.datasource.password"));
Assertions.assertEquals("8080", environment.getProperty("server.port")); Assertions.assertEquals("8080", environment.getProperty("server.port"));
} }
@Test
void decryptsEncryptedPlaceholderDefaultValuesBeforeBinding() {
ConfigTextEncryptor encryptor = ConfigTextEncryptor.withDefaultHardcodedKey();
String encryptedUsername = encryptor.encrypt("db-user");
StandardEnvironment environment = new StandardEnvironment();
environment.getPropertySources().addFirst(new MapPropertySource("test", Map.of(
"spring.datasource.username", "${TMS_DB_USER:" + encryptedUsername + "}"
)));
new ConfigDecryptingEnvironmentPostProcessor()
.postProcessEnvironment(environment, new SpringApplication(Object.class));
Assertions.assertEquals("db-user", environment.getProperty("spring.datasource.username"));
}
@Test
void springBootAutoLoadsPostProcessorFromFactories() {
ConfigTextEncryptor encryptor = ConfigTextEncryptor.withDefaultHardcodedKey();
String encryptedUsername = encryptor.encrypt("dev-user");
SpringApplication application = new SpringApplication(Object.class);
application.setWebApplicationType(WebApplicationType.NONE);
application.setDefaultProperties(Map.of(
"tms.crypto.test.username", encryptedUsername,
"spring.main.banner-mode", "off"
));
try (ConfigurableApplicationContext context = application.run()) {
Assertions.assertEquals("dev-user", context.getEnvironment().getProperty("tms.crypto.test.username"));
}
}
} }

View File

@ -18,7 +18,7 @@ class RestoreCommandInvokerTest {
Path logFile = tempDir.resolve("restore.log"); Path logFile = tempDir.resolve("restore.log");
int exitCode = invoker.run( int exitCode = invoker.run(
List.of("/bin/zsh", "-lc", "printf 'hello-restore'"), List.of("/bin/bash", "-lc", "printf 'hello-restore'"),
logFile, logFile,
tempDir tempDir
); );
@ -35,7 +35,7 @@ class RestoreCommandInvokerTest {
long started = System.nanoTime(); long started = System.nanoTime();
int exitCode = invoker.run( int exitCode = invoker.run(
List.of("/bin/zsh", "-lc", "sleep 2; printf 'done'"), List.of("/bin/bash", "-lc", "sleep 2; printf 'done'"),
logFile, logFile,
tempDir tempDir
); );
@ -46,12 +46,13 @@ class RestoreCommandInvokerTest {
} }
private static void waitUntilLogContains(Path logFile, String expected) throws Exception { private static void waitUntilLogContains(Path logFile, String expected) throws Exception {
long deadline = System.currentTimeMillis() + 1000; long deadline = System.currentTimeMillis() + 5000;
while (System.currentTimeMillis() < deadline) { while (System.currentTimeMillis() < deadline) {
if (Files.isRegularFile(logFile) && Files.readString(logFile).contains(expected)) { if (Files.isRegularFile(logFile) && Files.readString(logFile).contains(expected)) {
return; return;
} }
Thread.sleep(20); Thread.sleep(20);
} }
Assertions.fail("restore log did not contain expected text: " + expected);
} }
} }