Spring Boot + Nacos + 微服务中使用Jasypt加密配置

一、项目集成Jasypt完整方案

1. Maven依赖配置

xml 复制代码
<!-- pom.xml -->
<dependencies>
    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Nacos Config -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        <version>2022.0.0.0</version>
    </dependency>
    
    <!-- Jasypt 加密 -->
    <dependency>
        <groupId>com.github.ulisesbocchio</groupId>
        <artifactId>jasypt-spring-boot-starter</artifactId>
        <version>3.0.5</version>
    </dependency>
    
    <!-- MySQL驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    
    <!-- Druid连接池 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.2.20</version>
    </dependency>
</dependencies>

2. 配置文件结构

yaml 复制代码
# bootstrap.yml (或 bootstrap.properties)
# 此文件优先加载,用于Nacos配置
spring:
  application:
    name: user-service  # 微服务名称
  profiles:
    active: dev         # 环境标识
    
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848           # Nacos地址
        file-extension: yaml                   # 配置文件格式
        namespace: ${spring.profiles.active}  # 对应环境命名空间
        group: DEFAULT_GROUP
        extension-configs:
          - data-id: jasypt-config.yaml       # Jasypt专用配置
            group: COMMON_CONFIG
            refresh: true
          - data-id: datasource-common.yaml   # 数据源公共配置
            group: COMMON_CONFIG
            refresh: true

3. Nacos中的配置内容

jasypt-config.yaml
yaml 复制代码
# Nacos配置中心中存放
# 数据ID: jasypt-config.yaml
# 分组: COMMON_CONFIG

# Jasypt加密配置
jasypt:
  encryptor:
    # 加密密码(必须保护)
    # 生产环境建议通过环境变量或启动参数传入
    password: ${JASYPT_ENCRYPTOR_PASSWORD:mySecretKey123!}
    
    # 加密算法
    algorithm: PBEWithMD5AndDES  # 可选:PBEWithMD5AndDES, PBEWithSHA1AndDESede, PBEWITHHMACSHA512ANDAES_256
    
    # 密钥获取迭代次数
    key-obtention-iterations: 1000
    
    # 盐生成器类名
    salt-generator-classname: org.jasypt.salt.RandomSaltGenerator
    
    # 算法提供商(JDK版本相关)
    provider-name: SunJCE
    
    # IV(初始化向量)生成器
    iv-generator-classname: org.jasypt.iv.RandomIvGenerator
    
    # 输出格式,默认为base64
    string-output-type: base64
    
    # 加密前缀和后缀(用于识别加密值)
    property:
      prefix: "ENC("
      suffix: ")"
datasource-common.yaml
yaml 复制代码
# Nacos配置中心中存放
# 数据ID: datasource-common.yaml
# 分组: COMMON_CONFIG

# 数据源配置(使用Jasypt加密)
spring:
  datasource:
    # 动态数据库URL(可根据环境变化)
    url: jdbc:mysql://${db.host:localhost}:${db.port:3306}/${db.name:testdb}?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
    username: ENC(5yWhYHrJ8A+tskRZWk9ndQ==)          # 加密的用户名
    password: ENC(JlzqDblbW0quZarZfT3wFjL6J/5A8+FV)  # 加密的密码
    
    # Druid连接池配置
    druid:
      initial-size: 5
      min-idle: 5
      max-active: 20
      max-wait: 60000
      validation-query: SELECT 1
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      
      # 监控配置
      filter:
        stat:
          enabled: true
          log-slow-sql: true
          slow-sql-millis: 1000
        wall:
          enabled: true
      web-stat-filter:
        enabled: true
      stat-view-servlet:
        enabled: true
        url-pattern: /druid/*
        allow: 127.0.0.1
        deny: ''
        login-username: admin
        login-password: ENC(加密后的监控密码)

# 动态数据库配置(可在不同环境覆盖)
db:
  host: 10.28.15.36
  port: 3306
  name: sbh_admin
user-service-dev.yaml(环境特定配置)
yaml 复制代码
# Nacos配置中心中存放
# 数据ID: user-service-dev.yaml
# 分组: DEFAULT_GROUP

# 微服务特定配置
server:
  port: 8081

# Redis配置(加密示例)
spring:
  redis:
    host: localhost
    port: 6379
    password: ENC(w6lEwUyJ7zP4K1qL8M9n2Q==)  # Redis密码加密
    timeout: 3000ms

# 业务配置
user:
  service:
    max-retry-count: 3
    timeout: 5000
    api-key: ENC(A1B2C3D4E5F6G7H8I9J0K==)  # 第三方API密钥加密

4. 加密工具类

java 复制代码
package com.example.user.utils;

import org.jasypt.encryption.StringEncryptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * Jasypt加密解密工具类
 */
@Component
public class JasyptUtil {
    
    @Autowired
    private StringEncryptor stringEncryptor;
    
    /**
     * 加密方法
     * @param plainText 明文
     * @return 密文(带ENC()前缀)
     */
    public String encrypt(String plainText) {
        if (plainText == null || plainText.trim().isEmpty()) {
            return plainText;
        }
        String encrypted = stringEncryptor.encrypt(plainText);
        return "ENC(" + encrypted + ")";
    }
    
    /**
     * 解密方法
     * @param encryptedText 密文(可带ENC()前缀)
     * @return 明文
     */
    public String decrypt(String encryptedText) {
        if (encryptedText == null || encryptedText.trim().isEmpty()) {
            return encryptedText;
        }
        // 去除ENC()前缀
        if (encryptedText.startsWith("ENC(") && encryptedText.endsWith(")")) {
            encryptedText = encryptedText.substring(4, encryptedText.length() - 1);
        }
        return stringEncryptor.decrypt(encryptedText);
    }
    
    /**
     * 批量加密(用于初始化配置)
     */
    public void batchEncryptDemo() {
        String[] plainTexts = {
            "root", 
            "password123", 
            "myRedisPassword",
            "apiSecretKey2024"
        };
        
        for (String plainText : plainTexts) {
            String encrypted = encrypt(plainText);
            System.out.println("明文: " + plainText);
            System.out.println("密文: " + encrypted);
            System.out.println("解密验证: " + decrypt(encrypted));
            System.out.println("---");
        }
    }
}

5. Spring Boot配置类

java 复制代码
package com.example.user.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import javax.sql.DataSource;

/**
 * 数据源配置类
 */
@Configuration
public class DataSourceConfig {
    
    /**
     * 配置Druid数据源
     * 注意:@Value注解会自动解密ENC()包裹的值
     */
    @Bean
    public DataSource dataSource(Environment env) {
        // 从环境变量中获取已解密的配置
        String url = env.getProperty("spring.datasource.url");
        String username = env.getProperty("spring.datasource.username");
        String password = env.getProperty("spring.datasource.password");
        
        System.out.println("数据源配置已加载:");
        System.out.println("URL: " + url);
        System.out.println("用户名: " + username);
        // 生产环境不建议打印密码
        // System.out.println("密码: " + password);
        
        return DruidDataSourceBuilder.create()
                .url(url)
                .username(username)
                .password(password)
                .build();
    }
}

6. 启动类增强

java 复制代码
package com.example.user;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.Environment;
import com.example.user.utils.JasyptUtil;
import javax.sql.DataSource;
import java.sql.Connection;

@SpringBootApplication
public class UserServiceApplication implements CommandLineRunner {
    
    @Autowired
    private Environment env;
    
    @Autowired
    private JasyptUtil jasyptUtil;
    
    @Autowired
    private DataSource dataSource;
    
    public static void main(String[] args) {
        // 方式1:通过环境变量设置加密密码
        // System.setProperty("JASYPT_ENCRYPTOR_PASSWORD", "your-secret-key");
        
        // 方式2:通过启动参数设置加密密码
        // java -jar user-service.jar --jasypt.encryptor.password=your-secret-key
        
        SpringApplication.run(UserServiceApplication.class, args);
    }
    
    @Override
    public void run(String... args) throws Exception {
        // 验证配置解密是否成功
        System.out.println("=== Jasypt配置验证 ===");
        
        // 验证数据库连接
        try (Connection conn = dataSource.getConnection()) {
            System.out.println("✅ 数据库连接成功");
            System.out.println("数据库URL: " + env.getProperty("spring.datasource.url"));
            System.out.println("数据库用户: " + env.getProperty("spring.datasource.username"));
        } catch (Exception e) {
            System.err.println("❌ 数据库连接失败: " + e.getMessage());
        }
        
        // 验证加密解密功能
        String testText = "HelloJasypt";
        String encrypted = jasyptUtil.encrypt(testText);
        String decrypted = jasyptUtil.decrypt(encrypted);
        
        System.out.println("加密解密测试:");
        System.out.println("原文: " + testText);
        System.out.println("密文: " + encrypted);
        System.out.println("解密: " + decrypted);
        System.out.println("测试结果: " + (testText.equals(decrypted) ? "✅ 成功" : "❌ 失败"));
        
        // 显示当前使用的加密算法
        System.out.println("当前加密算法: " + 
            env.getProperty("jasypt.encryptor.algorithm", "默认算法"));
    }
}

7. Docker部署配置

dockerfile 复制代码
# Dockerfile
FROM openjdk:11-jre-slim

# 设置环境变量(用于传递加密密码)
ENV JASYPT_ENCRYPTOR_PASSWORD=""
ENV SPRING_PROFILES_ACTIVE="prod"

# 创建应用目录
RUN mkdir -p /app
WORKDIR /app

# 复制jar包
COPY target/user-service.jar app.jar

# 暴露端口
EXPOSE 8080

# 启动命令
ENTRYPOINT ["java", \
    "-Djava.security.egd=file:/dev/./urandom", \
    "-Dspring.profiles.active=${SPRING_PROFILES_ACTIVE}", \
    "-Djasypt.encryptor.password=${JASYPT_ENCRYPTOR_PASSWORD}", \
    "-jar", "app.jar"]

8. Kubernetes部署配置

yaml 复制代码
# k8s-deployment.yaml
apiVersion: v1
kind: Secret
metadata:
  name: jasypt-secret
type: Opaque
data:
  # 使用base64编码的加密密码(echo -n "myProdSecret2024!" | base64)
  encryptor-password: bXlQcm9kU2VjcmV0MjAyNCE=
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
spec:
  replicas: 2
  selector:
    matchLabels:
      app: user-service
  template:
    metadata:
      labels:
        app: user-service
    spec:
      containers:
      - name: user-service
        image: user-service:1.0.0
        ports:
        - containerPort: 8080
        env:
        - name: JASYPT_ENCRYPTOR_PASSWORD
          valueFrom:
            secretKeyRef:
              name: jasypt-secret
              key: encryptor-password
        - name: SPRING_PROFILES_ACTIVE
          value: "prod"
        resources:
          requests:
            memory: "512Mi"
            cpu: "250m"
          limits:
            memory: "1Gi"
            cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
  name: user-service
spec:
  selector:
    app: user-service
  ports:
  - port: 80
    targetPort: 8080
  type: ClusterIP

9. 命令行工具脚本

bash 复制代码
#!/bin/bash
# encrypt-tool.sh - Jasypt加密工具脚本

# 配置
JASYPT_ALGORITHM="PBEWithMD5AndDES"
JASYPT_PASSWORD="mySecretKey123!"
JASYPT_ITERATIONS="1000"

# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# 帮助信息
show_help() {
    echo "Jasypt加密工具"
    echo "使用方法: $0 [选项]"
    echo "选项:"
    echo "  encrypt <明文>   加密文本"
    echo "  decrypt <密文>   解密文本(可带ENC()前缀)"
    echo "  file <文件路径>  批量加密文件中的值"
    echo "  genkey           生成随机加密密钥"
    echo "  test             测试加密解密功能"
    echo "  help             显示此帮助信息"
}

# 加密函数
encrypt_text() {
    local plaintext="$1"
    if [ -z "$plaintext" ]; then
        echo -e "${RED}错误: 请输入要加密的文本${NC}"
        return 1
    fi
    
    # 使用Java加密
    ENCRYPTED=$(java -cp ".:jasypt-1.9.3.jar" \
        -Djasypt.encryptor.password="$JASYPT_PASSWORD" \
        -Djasypt.encryptor.algorithm="$JASYPT_ALGORITHM" \
        org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI \
        input="$plaintext" password="$JASYPT_PASSWORD" \
        algorithm="$JASYPT_ALGORITHM" \
        iterations="$JASYPT_ITERATIONS" | grep "ENC(")
    
    if [ -n "$ENCRYPTED" ]; then
        echo -e "${GREEN}加密成功:${NC}"
        echo "明文: $plaintext"
        echo "密文: $ENCRYPTED"
    else
        echo -e "${RED}加密失败${NC}"
    fi
}

# 生成加密配置
generate_config() {
    echo -e "${YELLOW}生成加密配置示例:${NC}"
    echo ""
    echo "# 数据库配置"
    echo "spring.datasource.username=ENC($(java -cp ".:jasypt-1.9.3.jar" \
        -Djasypt.encryptor.password="$JASYPT_PASSWORD" \
        org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI \
        input="root" password="$JASYPT_PASSWORD" \
        algorithm="$JASYPT_ALGORITHM" | grep -o "ENC([^)]*)"))"
    
    echo ""
    echo "# Redis配置"
    echo "spring.redis.password=ENC($(java -cp ".:jasypt-1.9.3.jar" \
        -Djasypt.encryptor.password="$JASYPT_PASSWORD" \
        org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI \
        input="redis123" password="$JASYPT_PASSWORD" \
        algorithm="$JASYPT_ALGORITHM" | grep -o "ENC([^)]*)"))"
}

# 主函数
main() {
    case "$1" in
        encrypt)
            encrypt_text "$2"
            ;;
        decrypt)
            # 解密逻辑类似
            ;;
        genkey)
            # 生成16位随机密钥
            echo -e "${GREEN}生成的随机密钥:${NC}"
            openssl rand -base64 16
            ;;
        test)
            echo -e "${YELLOW}测试加密解密...${NC}"
            TEST_TEXT="TestPassword@123"
            ENCRYPTED=$(encrypt_text "$TEST_TEXT" | grep "密文:" | cut -d' ' -f2)
            echo "测试完成"
            ;;
        help|*)
            show_help
            ;;
    esac
}

# 执行
main "$@"

10. 多环境密钥管理方案

properties 复制代码
# 不同环境使用不同的加密密钥
# 开发环境
dev.jasypt.password=dev_secret_2024!

# 测试环境
test.jasypt.password=test_secret_2024!

# 生产环境(从安全存储获取)
prod.jasypt.password=${VAULT:prod/jasypt/password}

# 在启动脚本中指定
#!/bin/bash
# startup.sh
ENV=$1
case $ENV in
    "dev")
        export JASYPT_ENCRYPTOR_PASSWORD="dev_secret_2024!"
        ;;
    "test")
        export JASYPT_ENCRYPTOR_PASSWORD="test_secret_2024!"
        ;;
    "prod")
        # 从HashiCorp Vault获取密钥
        export JASYPT_ENCRYPTOR_PASSWORD=$(vault read -field=password secret/prod/jasypt)
        ;;
    *)
        echo "Unknown environment"
        exit 1
        ;;
esac

java -jar user-service.jar

二、最佳实践建议

1. 安全建议

  • 生产环境加密密码不要写在代码或配置文件中
  • 使用环境变量或密钥管理服务(如Vault)传递加密密码
  • 定期轮换加密密钥
  • 不同环境使用不同的加密密钥

2. 微服务架构中的使用

yaml 复制代码
# 在微服务网关统一管理加密配置
# gateway-service配置
spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/user/**
          filters:
            - name: JasyptConfigFilter  # 自定义过滤器处理加密配置

3. 监控和告警

java 复制代码
// 监控Jasypt解密状态
@Component
public class JasyptHealthIndicator implements HealthIndicator {
    
    @Autowired
    private JasyptUtil jasyptUtil;
    
    @Override
    public Health health() {
        try {
            String test = "health_check";
            String encrypted = jasyptUtil.encrypt(test);
            String decrypted = jasyptUtil.decrypt(encrypted);
            
            if (test.equals(decrypted)) {
                return Health.up()
                    .withDetail("status", "Jasypt工作正常")
                    .withDetail("algorithm", "PBEWithMD5AndDES")
                    .build();
            } else {
                return Health.down()
                    .withDetail("error", "加密解密不匹配")
                    .build();
            }
        } catch (Exception e) {
            return Health.down()
                .withDetail("error", e.getMessage())
                .build();
        }
    }
}

这套方案提供了从开发到生产全链路的加密配置管理,适合微服务架构下的安全需求。

相关推荐
就叫飞六吧2 小时前
钉钉企业内部应用 SSO 免登集成实战 (Spring Boot 版)
java·spring boot·钉钉
moxiaoran57532 小时前
Go语言的数据类型转换
开发语言·后端·golang
IT_陈寒2 小时前
React 18 性能优化实战:5个被低估的Hooks用法让你的应用快30%
前端·人工智能·后端
乐茵lin2 小时前
golang context底层设计探究
开发语言·后端·golang·大学生·设计·context·底层源码
七夜zippoe2 小时前
Spring Boot Starter自定义开发 构建企业级组件库
java·spring boot·starter·自动装配·配置元
Trouvaille ~2 小时前
【C++篇】让错误被温柔对待(上):异常基础与核心机制
运维·开发语言·c++·后端·异常·基础入门·优雅编程
Victor3562 小时前
Hibernate(8)什么是Hibernate的SessionFactory?
后端
Victor3562 小时前
Hibernate(7)Hibernate的Session是什么?
后端
独断万古他化2 小时前
【Spring Web MVC 入门续篇】请求处理之 Cookie 与 Session 获取实战
后端·spring·java-ee·mvc