如何在Tomcat Web应用中加密/解密敏感属性
替代Jasypt的敏感字段加密方案及GitLab集成实践
一、替代Jasypt的可行方案
鉴于Jasypt维护停滞,推荐以下两种可控性强的方案:
1. 自定义加密工具类
自己实现轻量级对称加密(如AES),完全掌控加密逻辑,无需依赖第三方库。核心思路是:
- 编写加密工具,对敏感字段加密后写入
application.properties(用ENC(xxx)标记加密值) - 应用启动时,读取环境变量中的解密密钥,对标记的加密字段进行解密,注入到配置上下文
2. 利用Spring生态原生加密能力(Spring项目专属)
如果是Spring Boot/Spring MVC应用,可通过EnvironmentPostProcessor或自定义PropertySource实现解密,配合Spring Cloud Config的加密功能(若使用配置中心),无需额外引入第三方加密库。
二、GitLab变量集成的具体操作
结合你的环境限制(WAR包部署、systemctl启停、CI+本地部署),分两种场景实现:
1. CI流水线部署
- 步骤1:存储敏感变量:在GitLab项目的
Settings > CI/CD > Variables中添加DB_PASSWORD、API_TOKEN、ENCRYPT_KEY等变量,勾选Protected和Masked防止泄露。 - 步骤2:部署时注入配置:
方案A:直接生成明文敏感配置文件(适合无需加密存储配置的场景)
方案B:加密配置+传递密钥(适合需要加密存储配置的场景)deploy: stage: deploy script: # 生成生产环境配置文件 - echo "db.password=${DB_PASSWORD}" > application-prod.properties - echo "api.token=${API_TOKEN}" >> application-prod.properties # 上传到Tomcat服务器外部配置目录(避免打包进WAR) - scp application-prod.properties tomcat-user@your-server:/opt/tomcat/conf/ # 重启Tomcat - ssh tomcat-user@your-server "systemctl restart tomcat"
在CI脚本中将解密密钥注入Tomcat的环境变量:deploy: stage: deploy script: # 写入Tomcat的systemd override配置,添加解密密钥环境变量 - ssh tomcat-user@your-server "echo 'Environment=\"ENCRYPT_KEY=${ENCRYPT_KEY}\"' > /etc/systemd/system/tomcat.service.d/encrypt-key.conf" - ssh tomcat-user@your-server "systemctl daemon-reload" # 上传加密后的application-prod.properties到服务器 - scp src/main/resources/application-prod.properties tomcat-user@your-server:/opt/tomcat/conf/ - ssh tomcat-user@your-server "systemctl restart tomcat"
2. 本地手动部署
- 避免将敏感字段打包进WAR,在Tomcat的
conf目录下创建application-local-prod.properties,写入明文敏感值(仅限本地测试用,注意文件权限)。 - 若使用加密方案,启动Tomcat前临时设置解密密钥环境变量:
或者修改Tomcat的systemd配置文件临时添加环境变量,启动后及时移除。export ENCRYPT_KEY=your-local-secure-key systemctl start tomcat
三、Tomcat环境适配要点
- 外部化配置:将敏感配置放在Tomcat外部目录(如
conf/),通过Tomcat的shared.loader配置加载,避免敏感信息打包进WAR。 - 权限控制:确保服务器上的敏感配置文件权限为
600,仅Tomcat用户可读:chmod 600 /opt/tomcat/conf/application-prod.properties chown tomcat:tomcat /opt/tomcat/conf/application-prod.properties - 密钥安全:本地部署时密钥通过环境变量传递,不要硬编码;CI部署时用GitLab受保护变量,禁止在日志中输出密钥。
四、自定义AES加密实现示例(Spring项目)
1. 加密工具类
import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import java.util.Base64; public class AESUtils { private static final String ALGORITHM = "AES"; public static String encrypt(String plainText, String key) throws Exception { SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), ALGORITHM); Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, keySpec); return Base64.getEncoder().encodeToString(cipher.doFinal(plainText.getBytes())); } public static String decrypt(String encryptedText, String key) throws Exception { SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), ALGORITHM); Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, keySpec); return new String(cipher.doFinal(Base64.getDecoder().decode(encryptedText))); } }
2. 配置解密处理器
import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.core.env.PropertySource; import org.springframework.core.env.StandardEnvironment; import javax.annotation.PostConstruct; import java.util.HashMap; import java.util.Map; @Configuration public class EncryptedPropertyProcessor { private final Environment environment; public EncryptedPropertyProcessor(Environment environment) { this.environment = environment; } @PostConstruct public void processEncryptedProperties() { if (!(environment instanceof StandardEnvironment standardEnv)) { return; } String encryptKey = System.getenv("ENCRYPT_KEY"); if (encryptKey == null) { throw new IllegalStateException("ENCRYPT_KEY environment variable is required for decryption"); } Map<String, Object> decryptedProps = new HashMap<>(); // 处理数据库密码 processEncryptedField("db.password", encryptKey, decryptedProps); // 处理API令牌 processEncryptedField("api.token", encryptKey, decryptedProps); // 将解密后的配置加入到环境变量最前面,优先读取 standardEnv.getPropertySources().addFirst(new PropertySource<>("decryptedProperties") { @Override public Object getProperty(String name) { return decryptedProps.get(name); } }); } private void processEncryptedField(String propertyName, String key, Map<String, Object> decryptedProps) { String encryptedValue = environment.getProperty(propertyName); if (encryptedValue == null || !encryptedValue.startsWith("ENC(") || !encryptedValue.endsWith(")")) { return; } try { String plainValue = AESUtils.decrypt(encryptedValue.substring(4, encryptedValue.length() - 1), key); decryptedProps.put(propertyName, plainValue); } catch (Exception e) { throw new RuntimeException("Failed to decrypt property: " + propertyName, e); } } }
3. 加密后的配置示例
在application-prod.properties中写入:
db.password=ENC(your-encrypted-db-password) api.token=ENC(your-encrypted-api-token)
内容的提问来源于stack exchange,提问作者pbuchheit
相关产品推荐
相关产品推荐

