Skip to content
SpringBoot集成阿里沙箱支付
  1. pom依赖项

    xml
    <!--沙箱支付完整依赖,解决链接中subject存在中文导致乱码的问题-->
    <dependency>
        <groupId>com.alipay.sdk</groupId>
        <artifactId>alipay-sdk-java</artifactId>
        <version>4.35.150.ALL</version>
    </dependency>
    <!--ConfigurationProperties注解依赖,解决yaml或者properties配置文件注入对象时提示配置,这是官方的配置依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
SpringBoot集成jasypt对yml敏感的数据进行加密
  1. pom依赖

    xml
    <dependency>
        <groupId>com.github.ulisesbocchio</groupId>
        <artifactId>jasypt-spring-boot-starter</artifactId>
        <version>3.0.4</version>
    </dependency>
  2. 添加yml配置信息

    yaml
    jasypt:
      encryptor:
        # 加密的秘钥
        password: qnmdmyy
        # 加密算法
        algorithm: PBEWithMD5AndDES或者PBEWITHHMACSHA512ANDAES_256
        iv-generator-classname: org.jasypt.iv.RandomIvGenerator
        property:
          # 算法识别的前后缀,默认ENC(),包含在前后缀的加密信息,会使用指定算法解密
          prefix: Enc(
          suffix: )
    • jasypt3.0后,默认支持的算法为 PBEWITHHMACSHA512ANDAES_256 ,该算法需要JDK1.9 以上支持或者添加JCE(Java Cryptography Extension 无限强度权限策略文件)支持。

    • 两种密码加密的方式还是有区别的,如果追求高安全性和性能,推荐使用PBEWITHHMACSHA512ANDAES_256

    • 如何添加JCE?

    • 获取JCE文件

      JDK6密码扩展无限制权限策略文件
      http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html
      
      JDK7密码扩展无限制权限策略文件
      https://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
      
      JDK8密码扩展无限制权限策略文件
      https://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
  3. 生成加密数据工具类

    java
    // PBEWithMD5AndDES算法所需要的工具类
    public class JasyptUtils {
        public static void main(String[] args) {
            String info = encrypt("root");
            System.out.println(info);
        }
    
        /**
         * 加密
         *
         * @param plaintext 明文    
         * @return
         */
        public static String encrypt(String plaintext) {
            StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
            EnvironmentStringPBEConfig config = new EnvironmentStringPBEConfig();
            // 指定算法
            config.setAlgorithm("PBEWithMD5AndDES");
            // 指定秘钥,和yml配置文件中保持一致
            config.setPassword("qnmdmyy");
            encryptor.setConfig(config);
            // 生成加密数据
            return encryptor.encrypt(plaintext);
        }
    
        /**
         * 解密
         *
         * @param data 加密后数据
         * @return
         */
        public static String decrypt(String data) {
            StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
            EnvironmentStringPBEConfig config = new EnvironmentStringPBEConfig();
            config.setAlgorithm("PBEWithMD5AndDES");
            config.setPassword("qnmdmyy");
            encryptor.setConfig(config);
            // 解密数据
            return encryptor.decrypt(data);
        }
    }
    
    // PBEWITHHMACSHA512ANDAES_256算法所需工具类
    public class JasyptUtil {
    
        public static void main(String[] args) {
        }
    
        /**
         * 加密
         *
         * @param plaintext 明文
         * @return
         */
        public static String encrypt(String plaintext) {
    
            // 1. 创建加解密工具实例
            PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
            // 2. 加解密配置
            SimpleStringPBEConfig config = new SimpleStringPBEConfig();
            config.setPassword("qnmdmyy");
            config.setAlgorithm("PBEWITHHMACSHA512ANDAES_256");
            // 为减少配置文件的书写,以下都是 Jasyp 3.x 版本,配置文件默认配置
            config.setKeyObtentionIterations( "1000");
            config.setPoolSize("1");
            config.setProviderName("SunJCE");
            config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
            config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
            config.setStringOutputType("base64");
            encryptor.setConfig(config);
            // 3. 加密
            return encryptor.encrypt(plaintext);
        }
    
        /**
         * 解密
         *
         * @param data 加密后数据
         * @return
         */
        public static String decrypt(String data) {
    
            // 1. 创建加解密工具实例
            PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
            // 2. 加解密配置
            SimpleStringPBEConfig config = new SimpleStringPBEConfig();
            config.setPassword("qnmdmyy");
            config.setAlgorithm("PBEWITHHMACSHA512ANDAES_256");
            // 为减少配置文件的书写,以下都是 Jasyp 3.x 版本,配置文件默认配置
            config.setKeyObtentionIterations( "1000");
            config.setPoolSize("1");
            config.setProviderName("SunJCE");
            config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
            config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
            config.setStringOutputType("base64");
            encryptor.setConfig(config);
            // 3. 加密
            return encryptor.decrypt(data);
    
        }
    
    }
  4. 执行加密算法并在yml中替换敏感信息

    图片

    图片

  5. 对加密秘钥进行处理: 加密秘钥在yml文件中明文存储是不安全的

    1. 在idea中测试时,可以进行如下配置

      图片

    2. 打包部署可以使用以下两个指令

      #方式1:
      java -jar xxx.jar -Djasypt.encryptor.password=qnmdmyy
      
      #方式2:
      java -jar xxx.jar --jasypt.encryptor.password=qnmdmyy
      • 既然使用指令配置秘钥,那么在docker构建项目镜像的时候就需要对以下原命令进行相应的修改

加密后的敏感数据模糊查询
  • 业务场景举例: 订单号在后台生成以后通过非对称加密存储在数据库中,前台通过输入部分字符对订单号进行模糊查询

  • 实现流程

    1. 首先后台生成一个长度为Len的订单号,本次示例Len为5

    2. 将生成的订单号通过公钥进行加密存储到订单表

    3. 步骤2的同时,将生成的订单号原文通过分片对称加密存储到订单号映射表

  • 表结构(简化)

    sql
    CREATE TABLE `t_order` (
      `id` varchar(36) NOT NULL,
      `order_no` varchar(2000) NOT NULL,
      `subject_name` varchar(500) NOT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
    
    CREATE TABLE `t_orderno_match` (
      `id` varchar(36) NOT NULL,
      `encrypt_piece_content` varchar(2000) NOT NULL,
      `encrypt_piece_length` int(10) DEFAULT NULL,
      `order_id` varchar(36) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
  • 功能实现代码

    java
    import org.apache.tomcat.util.codec.binary.Base64;
    
    import javax.crypto.Cipher;
    import java.security.KeyFactory;
    import java.security.PrivateKey;
    import java.security.PublicKey;
    import java.security.spec.PKCS8EncodedKeySpec;
    import java.security.spec.X509EncodedKeySpec;
    
    /**
     * Description: 非对称加解密工具类
     * Author: ZhangHL
     * Version: 1.0
     * Create Date Time: 2023/6/20 15:18.
     */
    public class RsaUtil {
    
        private static String publicKey =
     "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1KTu4JJ70M5ZLuD1V8m2Lrq2L//FoY4TKOAlUC4kh1Nu+4J+CpUeIyKgpqGZnPIhyzCT64NXP/08wDTCUqO/SkJEmTSkLSw3ZsEY/KQ5OuasW+HQTuv8TggLmjqP1KCqLsvyfEwuyUP+P1r5dHjqa13zqMadgd5V9X8uJA3Eezcy4xJqjWkCQWKmYS5GEiY9uDLCcoB3LMG3SpO2a41K38HXkyrXAxy2+JEjbFVdACrIlykHy1navdeWjzB1bD/efaa/o6LO66Oaxg96acyhY4lyzrY6XyY2QKxC9JC5q05dqUPYDMR6tpdSLoIgJb5Wq/pZKEyAWefsLbHcxtuNhsXpl4c7gqcxWj7hhrWmqYY2V16Cc6M02BdKgBVhV8R0RGDUgrd9JsiAhdQraD5H1+FfcQZ6QpM+VQ5ESZWKnAnH2sCQEjBUj65D1GM/bD619N5FeOfjbPXSjfCDj7jzu8a/i0ovntumgZP2Cuv6r8Hy+U9R1LFt/fl+BNWBo1OxSXFYr0CSFMPBF80BeWBdi3VluJtTlW0khPVyxeJq1HgfHGXkvVKwx3nz4uhMIM1XIKFAqdpBoG8JghVnhP4oxnmM0ca2QY8f0kRYW67BNMpwLZWRHRgQHrj6ATw1Pd9jAsXdiQgQhkxTdhVnjuwRrHnRRSDVOzeLULvON1aFHr8CAwEAAQ==";
        private static String privateKey =
     "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";
    
        /***
         * description: 公钥加密
         * @author: ZhangHL
         * @param text
         * @return:
        */
        public static String encryptByPublicKey(String text) throws Exception{
            X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec2);
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            byte[] result = cipher.doFinal(text.getBytes());
            return Base64.encodeBase64String(result);
        }
    
        /***
         * description: 私钥解密
         * @author: ZhangHL
         * @param text
         * @return:
        */
        public static String decryptByPrivateKey(String text) throws Exception {
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            byte[] result = cipher.doFinal(Base64.decodeBase64(text));
            return new String(result);
        }
    }
    java
    import javax.crypto.Cipher;
    import javax.crypto.spec.SecretKeySpec;
    import java.nio.charset.StandardCharsets;
    import java.security.Key;
    import java.util.Base64;
    
    /**
     * Description: 对称加密工具类-javax.crypto实现方式
     * Author: ZhangHL
     * Version: 1.0
     * Create Date Time: 2023/10/10 11:08.
     */
    public class SymmetryUtil {
        private static final String ALGORITHM = "AES";
        private static final String KEY = "0123456789abcdef"; // 密钥 16 字节长
    
        public static String encrypt(String plaintext) throws Exception {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            Key key = new SecretKeySpec(KEY.getBytes(StandardCharsets.UTF_8), ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] cipherBytes = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(cipherBytes);
        }
    
        public static String decrypt(String encryptedString) throws Exception {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            Key key = new SecretKeySpec(KEY.getBytes(StandardCharsets.UTF_8), ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] cipherBytes = Base64.getDecoder().decode(encryptedString);
            byte[] plaintextBytes = cipher.doFinal(cipherBytes);
            return new String(plaintextBytes, StandardCharsets.UTF_8);
        }
    }
    java
    /**
     * Description: 加密映射表公共属性
     * Author: ZhangHL
     * Version: 1.0
     * Create Date Time: 2024/1/6 18:49.
     */
    public class BaseEncryptMatch {
    
        private String id;
        private String encryptPieceContent;
        private Integer encryptPieceLength;
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getEncryptPieceContent() {
            return encryptPieceContent;
        }
    
        public void setEncryptPieceContent(String encryptPieceContent) {
            this.encryptPieceContent = encryptPieceContent;
        }
    
        public Integer getEncryptPieceLength() {
            return encryptPieceLength;
        }
    
        public void setEncryptPieceLength(Integer encryptPieceLength) {
            this.encryptPieceLength = encryptPieceLength;
        }
    
        public BaseEncryptMatch(String id, String encryptPieceContent, Integer encryptPieceLength) {
            this.id = id;
            this.encryptPieceContent = encryptPieceContent;
            this.encryptPieceLength = encryptPieceLength;
        }
    
        @Override
        public String toString() {
            return "BaseEncryptMatch{" +
                    "id='" + id + '\'' +
                    ", encryptPieceContent='" + encryptPieceContent + '\'' +
                    ", encryptPieceLength=" + encryptPieceLength +
                    '}';
        }
    }
    
    /**
     * Description: 订单号加密映射实体类
     * Author: ZhangHL
     * Version: 1.0
     * Create Date Time: 2024/1/6 19:11.
     */
    public class OrderEncryptMatch extends BaseEncryptMatch{
    
        private String orderId;
    
        public String getOrderId() {
            return orderId;
        }
    
        public void setOrderId(String orderId) {
            this.orderId = orderId;
        }
    
        // 注意!!!!!一定要有和BaseEncryptMatch相同参数的构造方法
        public OrderEncryptMatch(String id, String encryptPieceContent, Integer encryptPieceLength) {
            super(id, encryptPieceContent, encryptPieceLength);
        }
    
        @Override
        public String toString() {
            return "OrderEncryptMatch{" +
                    "orderId='" + orderId + '\'' +
                    "} " + super.toString();
        }
    }
    java
    import com.zhl.common.entity.BaseEncryptMatch;
    
    import java.lang.reflect.Constructor;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.UUID;
    
    /**
     * Description: 加密相关工具类
     * Author: ZhangHL
     * Version: 1.0
     * Create Date Time: 2024/1/6 17:28.
     */
    public class EncryptUtil {
    
        /***
         * description: 分片加密
         * 因为不止订单号会加密存储,所以在对敏感内容进行加密的时候,使用泛型保证了广泛性,
         * 任何加密内容映射表属性除了自身业务属性以外,都必须含有BaseEncryptMatch的三个属性
         * @author: ZhangHL
         * @param rawText
         * @return:
        */
        public static <T extends BaseEncryptMatch> List<T> pieceEncrypt(String rawText, Class<T> classType) throws Exception {
            List<T> resultList = new ArrayList<>();
    
            for (int i = 1; i <= rawText.length(); i++) {
                for (int j = 0; j < (rawText.length() - i + 1); j++) {
    
                    Constructor<T> constructor = classType.getConstructor(String.class, String.class, Integer.class);
                    T t = constructor.newInstance(
                            UUID.randomUUID().toString(),
                            SymmetryUtil.encrypt(rawText.substring(j, i + j)),
                            i
                    );
    
                    resultList.add(t);
                }
            }
    
            return resultList;
        }
    
    }
    java
    // 订单逻辑待补充
    java
    // 订单查询逻辑待补充
定时任务&&锁

MIT版权,未经许可禁止任何形式的转载