This article is excerpted from the book “Spring Cloud Microservice Introduction and Progress”. For some important configuration information, such as sensitive configurations such as passwords, we want to encrypt the configurations and store them to ensure security. Apollo framework itself does not provide the function of data encryption. There are two ways to achieve the function of data encryption. The first way is to change the source code of Apollo and add the logic of encryption and decryption.
Jasypt-spring-boot is a framework developed based on Spring Boot, which can automatically decrypt the encrypted content in properties. In Apollo, jasypt-Spring-Boot framework can also be used to realize data encryption and decryption operations.
Jasypt-spring-boot GitHub address: github.com/ulisesbocch…
Encrypt the configuration we need to encrypt through the method provided by Jasypt-spring-boot, and then configure the encrypted content in Apollo. When the project is started, Jasypt-spring-boot will decrypt the configuration encrypted by Apollo. So that users can obtain the decrypted content.
Create a new Maven project with Apollo and Jasypt dependencies:
<dependency> <groupId>com.ctrip.framework.apollo</groupId> <artifactId>apollo-client</artifactId> The < version > 1.1.0 < / version > < / dependency > <! --> <dependency> <groupId>com.github. Ulisesbocchio </groupId> < artifactId > jasypt - spring - the boot - starter < / artifactId > < version > 1.16 < / version > < / dependency >Copy the code
Add the following dependency information:
server.port=8081
app.id=SampleApp
apollo.meta=http://localhost:8080
apollo.bootstrap.enabled=true
apollo.bootstrap.namespaces=application
jasypt.encryptor.password=yinjihaunkey
Copy the code
- Jasypt. The encryptor. Password: configure encryption Key
Create an encrypted utility class for encrypted configuration:
Public class EncryptUtil {/** * PATTERN */ private static PATTERN BLANK_PATTERN = pattern.compile ("\\s*|\t|\r|\n"); /** * private static String PASSWORD ="yinjihaunkey"; /** * private static String ALGORITHM ="PBEWithMD5AndDES"; public static Map<String, String> getEncryptedParams(String Input) {ByteArrayOutputStream ByteArrayOutputStream = new ByteArrayOutputStream(1024); PrintStream cacheStream = new PrintStream(byteArrayOutputStream); System.setout (cacheStream); String[] args = {"input=" + input, "password=" + PASSWORD, "algorithm="+ ALGORITHM}; JasyptPBEStringEncryptionCLI.main(args); / / perform the output of encrypted String message = byteArrayOutputStream. ToString (); String str = replaceBlank(message); int index = str.lastIndexOf("-"); // Return encrypted data Map<String, String> result = new HashMap<String, String>(); result.put("input", str.substring(index + 1));
result.put("password", PASSWORD);
returnresult; } /** * replaces tabs, Spaces, and newlines ** @param STR * @return
*/
private static String replaceBlank(String str) {
String dest = "";
if(! StringUtils.isEmpty(str)) { Matcher matcher = BLANK_PATTERN.matcher(str); dest = matcher.replaceAll("");
}
return dest;
}
public static void main(String[] args) {
System.out.println(getEncryptedParams("hello")); }}Copy the code
Executing the main method yields the following output:
{input=0JK4mrGjPUxkB4XuqEv2YQ==, password=yinjihaunkey}
Copy the code
Input is the encrypted content of Hello. Copy the value of the input to Apollo and store it in a format that follows certain rules:
test.input = ENC(0JK4mrGjPUxkB4XuqEv2YQ==)
Copy the code
The encrypted content needs to be wrapped in ENC so jasypt can decrypt the value.
Where you can inject configuration directly by name, for example:
@Value("${test.input}")
private String input;
Copy the code
The value of the input is the decrypted value, the user does not need to care about the decryption logic, jasypt framework takes care of it internally.
Jasypt integration Apollo also has some shortcomings, so far I only found the following problems:
-
Values in the project are not refreshed after they are modified in the configuration center
-
The value obtained by injecting the Config object cannot be decrypted
@ApolloConfig
private Config config;
@GetMapping("/config/getUserName3")
public String getUserName3() {
return config.getProperty("test.input"."yinjihuan");
}
Copy the code
The two questions listed above, with the realization of the jasypt way is to have a relationship, means the encrypted password may only suitable for a database, such as the startup can decrypt, and only used once, if it is some more core business configuration need encryption, jasypt is cannot support, can’t updated in real time. In the next section, I’ll show you how to modify Apollo’s source code to address both of these issues.
Extended Apollo support for storage encryption and decryption
The previous section showed you how to use JASypt to encrypt and decrypt configurations in Apollo. The basic requirements can be implemented, but there are still some shortcomings.
Jasypt simply decrypts Spring configurations in the ENC (XX) format at startup and cannot be updated when the configuration changes. Because the Apollo framework itself does not have this configuration encryption and decryption function, if we want to achieve encryption and decryption, and can dynamically update, we need to make some modifications to Apollo source code to meet the requirements.
The source code changes also need to be repackaged, the author here introduces a relatively simple implementation, is to create the same class name as the Apollo framework to override, so that there is no need to replace the client is already in use.
If the content stored in the configuration center is encrypted, it means that the configuration pulled by the Apollo client from the configuration center is also encrypted. We need to decrypt the configuration after the configuration is pulled, and then go through the subsequent process, such as binding to Spring. After this service point is entered, the content encrypted by the configuration center can be automatically converted into the decrypted plaintext, transparent to the user.
Through the analysis of the source of Apollo, the author found a the most appropriate entry point to do this, this class is com. Ctrip. Framework. Apollo. Internals. DefaultConfig, DefaultConfig is Coonfig interface implementation class, Configuration initialization and retrieval are handled by DefaultConfig.
DefaultConfig has a configuration update method called updateConfig that decrypts the encrypted data:
private void updateConfig(Properties newConfigProperties, ConfigSourceType sourceType) {
Set<Object> keys = newConfigProperties.keySet();
for(Object k : keys) { String key = k.toString(); String value = newConfigProperties.getProperty(key); / / encrypted Valueif (value.startsWith("ENC(") && value.endsWith(")")) {
logger.debug("Encrypt Value {}", value); / / decrypt and then try to assignment {String decryptValue = AesEncryptUtils. AesDecrypt (value. The substring (3, the value. The length () - 1), DECRYPT_KEY); newConfigProperties.setProperty(key, decryptValue); } catch (Exception e) { logger.error("Encryption configuration decryption failed", e);
}
}
}
m_configProperties.set(newConfigProperties);
m_sourceType = sourceType;
}
Copy the code
AES is used for decryption here, which means that the encrypted content in the configuration center also needs to be encrypted with the same encryption algorithm. As for the format, ENC(XX) is still used to identify this is an encrypted configuration content. After decryption, the decrypted plaintext content is reassigned to Properties, leaving the rest of the process unchanged.
Create an encryption test class, encrypt the configuration content, copy and store it in Apollo
public class Test {
public static void main(String[] args) {
String msg = "hello yinjihaun";
try {
String encryptMsg = AesEncryptUtils.aesEncrypt(msg, "1111222233334444"); System.out.println(encryptMsg); } catch (Exception e) { e.printStackTrace(); }}}Copy the code
The output is as follows:
Ke4LIPGOp3jCwbIHtmhmBA==
Storage in Apollo requires ENC to wrap the encrypted content as follows:
test.input = ENC(Ke4LIPGOp3jCwbIHtmhmBA==)
The previous code is still used for testing. The method of Config acquisition and Spring injection can successfully obtain the decrypted data, and it can also be pushed to the client for successful decryption in real time after modification by the configuration center.
This article is excerpted from the book “Spring Cloud Microservice Introduction and Progress”.