微服务SpringBoot整合Jasypt加密工具
作者:mmseoamin日期:2024-02-04

微服务SpringBoot整合Jasypt加密工具,在这里插入图片描述,第1张

文章目录

    • 一、Jasypt介绍
    • 二、Jasypt手动使用
      • 2.1 密码加密场景
      • 2.2 文本加密场景
      • 2.3 数值加密场景
      • 2.4 二进制数据加密场景
      • 三、Jasypt整合SpringBoot
      • 四、生成环境启动
      • 无、参考文档

        一、Jasypt介绍

        Jasypt是Java加密工具包,能支持对密码的哈希加密,对文本和二进制数据的对称加解密,还能集成SpringBoot项目对配置文件中的密钥进行加密存储。

        引入依赖如下:

        
        
            com.github.ulisesbocchio
            jasypt-spring-boot-starter
            3.0.4
        
        

        二、Jasypt手动使用

        2.1 密码加密场景

        用户注册账户的时候需要输入密码,我们将密码加密后保存到数据库中,保证用户的敏感数据的安全性。当用户再次登录的时候,我们需要将登录密码和注册时保存的密文密码进行比对,只有比对一致才能完成登录。

        密码加密工具类主要有三个,它们都是实现了PasswordEncryptor接口,下面我们逐步来看。

        @Slf4j
        @RestController
        public class SignController {
            private final BasicPasswordEncryptor basicPasswordEncryptor = new BasicPasswordEncryptor();
            private String encryptedPassword = null;
            @GetMapping("/signup/{password}")
            public String signup(@PathVariable String password){
                log.info("用户注册密码为:{}", password);
                encryptedPassword = basicPasswordEncryptor.encryptPassword(password);
                log.info("用户注册密码加密后为:{}", encryptedPassword);
                return encryptedPassword;
            }
            @GetMapping("/signin/{password}")
            public String signin(@PathVariable String password){
                log.info("用户登录密码为:{}", password);
                if(basicPasswordEncryptor.checkPassword(password, encryptedPassword)){
                    log.info("用户登录成功!");
                    return "success";
                }
                log.info("用户登录失败!");
                return "fail";
            }
        }
        

        启动项目后,我们首先注册用户密码localhost:8080/signup/123456,就能得到密文5b32ygn5pbBvphjIKco6X8Z2VfWqwEUw,并将其保存到类变量中暂存,当我们再次登录localhost:8080/signin/123456,就能登录成功了。相反的,如果登录时密码随意输错,就会登录失败。

        2022-10-11 15:41:57.038  INFO 26268 --- [nio-8080-exec-1] c.e.myapp.controller.SignController      : 用户注册密码为:123456
        2022-10-11 15:41:57.039  INFO 26268 --- [nio-8080-exec-1] c.e.myapp.controller.SignController      : 用户注册密码加密后为:5b32ygn5pbBvphjIKco6X8Z2VfWqwEUw
        2022-10-11 15:42:07.405  INFO 26268 --- [nio-8080-exec-3] c.e.myapp.controller.SignController      : 用户登录密码为:123456
        2022-10-11 15:42:07.406  INFO 26268 --- [nio-8080-exec-3] c.e.myapp.controller.SignController      : 用户登录成功!
        2022-10-11 15:42:12.767  INFO 26268 --- [nio-8080-exec-4] c.e.myapp.controller.SignController      : 用户登录密码为:123457
        2022-10-11 15:42:12.767  INFO 26268 --- [nio-8080-exec-4] c.e.myapp.controller.SignController      : 用户登录失败!
        

        那么这种加密方式是什么呢?我们可以打开BasicPasswordEncryptor的源码,看到类上面的注释:

        • Algorithm: MD5.
        • Salt size: 8 bytes.
        • Iterations: 1000.

          意思就是使用的MD5这种哈希算法,并且使用8字节(64位)的盐值,迭代计算1000次得到的密文。

          除了使用如上的BasicPasswordEncryptor工具之外,还有StrongPasswordEncryptor工具类,它的加密登记更加的安全:

          • Algorithm: SHA-256.
          • Salt size: 16 bytes.
          • Iterations: 100000.

            如果这些加密算法都不能满足你的要求,就可以使用ConfigurablePasswordEncryptor来自定义加密工具类ConfigurablePasswordEncryptor,可以设置自己需要使用的算法。

            总结:

            接口类PasswordEncryptor主要有如下三个实现类:

            • BasicPasswordEncryptor,使用MD5算法;
            • StrongPasswordEncryptor,使用SHA-256算法;
            • ConfigurablePasswordEncryptor,可自定义指定哈希算法;

              哈希算法是不可逆的,因此只有加密encryptPassword和检查checkPassword两个方法。

              2.2 文本加密场景

              用户的手机号、身份证号等敏感信息在存储的时候需要进行加密,但是这些敏感数据在需要使用的时候是需要明文解密的,因此不适合使用2.1节的哈希算法,而是使用对称加密的形式。

              文本加密工具类主要有三个,它们都是实现了TextEncryptor接口,下面我们逐步来看。

              @Slf4j
              @RestController
              public class TextController {
                  private static final BasicTextEncryptor basicTextEncryptor = new BasicTextEncryptor();
                  private static final String SECRET = "hello";
                  private String encryptedText = null;
                  static {
                      basicTextEncryptor.setPassword(SECRET);
                  }
                  @GetMapping("/encryptText/{plainText}")
                  public String encryptText(@PathVariable String plainText){
                      log.info("用户输入明文:{}", plainText);
                      encryptedText = basicTextEncryptor.encrypt(plainText);
                      log.info("用户加密密文:{}", encryptedText);
                      return encryptedText;
                  }
                  @GetMapping("/decryptText")
                  public String decryptText(){
                      String plainText = basicTextEncryptor.decrypt(encryptedText);
                      log.info("用户原始明文:{}", plainText);
                      return plainText;
                  }
              }
              

              项目启动后,我们分别访问localhost:8080/encryptText/hello进行加密,访问localhost:8080/decryptText进行解密。

              2022-10-11 15:52:36.949  INFO 21652 --- [nio-8080-exec-1] c.e.myapp.controller.TextController      : 用户输入明文:hello
              2022-10-11 15:52:36.950  INFO 21652 --- [nio-8080-exec-1] c.e.myapp.controller.TextController      : 用户加密密文:u/qYluhyFpyOA6xMD3z3JA==
              2022-10-11 15:52:46.345  INFO 21652 --- [nio-8080-exec-2] c.e.myapp.controller.TextController      : 用户原始明文:hello
              

              我们同样打开BasicTextEncryptor可以看到它的加密原理:

              • Algorithm: PBEWithMD5AndDES.
              • Key obtention iterations: 1000.

                同样的,我们可以使用安全性更高的StrongTextEncryptor:

                • Algorithm: PBEWithMD5AndTripleDES.
                • Key obtention iterations: 1000.

                  还有安全性更高的AES256TextEncryptor:

                  • Algorithm: PBEWithHMACSHA512AndAES_256".
                  • Key obtention iterations: 1000.

                    2.3 数值加密场景

                    如果需要对整数或者小数进行加密,就可以分别使用IntegerNumberEncryptor接口和DecimalNumberEncryptor接口的实现类。同样的,这种场景的加密也都是对称加密,用法完全一样。

                    IntegerNumberEncryptor:主要用来对整数进行加解密。

                    • BasicIntegerNumberEncryptor
                      • Algorithm: PBEWithMD5AndDES.
                      • Key obtention iterations: 1000.
                      • StrongIntegerNumberEncryptor
                        • Algorithm: PBEWithMD5AndTripleDES.
                        • Key obtention iterations: 1000.
                        • AES256IntegerNumberEncryptor
                          • Algorithm: PBEWithHMACSHA512AndAES_256.
                          • Key obtention iterations: 1000.

                            DecimalNumberEncryptor:主要用来对小数进行加解密。

                            • BasicDecimalNumberEncryptor
                              • Algorithm: PBEWithMD5AndDES.
                              • Key obtention iterations: 1000.
                              • StrongDecimalNumberEncryptor
                                • Algorithm: PBEWithMD5AndTripleDES.
                                • Key obtention iterations: 1000.
                                • AES256DecimalNumberEncryptor
                                  • Algorithm: PBEWithHMACSHA512AndAES_256.
                                  • Key obtention iterations: 1000.

                                    2.4 二进制数据加密场景

                                    暂未遇到需要加密二进制数据的业务场景,此处略过,使用方法可以参考官网。

                                    三、Jasypt整合SpringBoot

                                    SpringBoot应用中有很多密钥和密码都是存储在配置文件中的,我们需要将它们以密文的方式存储起来。

                                    # 服务器配置
                                    server:
                                      port: 8080
                                    # Spring配置
                                    spring:
                                      # 数据源配置
                                      datasource:
                                        driver-class-name: com.mysql.cj.jdbc.Driver
                                        url: jdbc:mysql://localhost:3306/test?characterEncoding=utf-8&&serverTimezone=Asia/Shanghai&&useSSL=false
                                        username: root
                                        # 此处是密码的密文,要用ENC()进行包裹
                                        password: ENC(KZeGx0ixuy4UrBp1HuhiDNnKB0cJr0cW)
                                    # mybatis配置
                                    mybatis:
                                      mapper-locations: classpath:mapper/*Mapper.xml
                                    # 加密配置
                                    jasypt:
                                      encryptor:
                                        # 指定加密密钥,生产环境请放到启动参数里面
                                        password: your-secret
                                        # 指定解密算法,需要和加密时使用的算法一致
                                        algorithm: PBEWithMD5AndDES
                                        # 指定initialization vector类型
                                        iv-generator-classname: org.jasypt.iv.NoIvGenerator
                                    

                                    如上是对数据库密码进行加密存储,密文是怎么的来的?可以写一个测试类,使用第二节介绍的内容自己手动加密。

                                    @Slf4j
                                    public final class JasyptUtils {
                                        /**
                                         * 加密使用密钥
                                         */
                                        private static final String PRIVATE_KEY = "lybgeek";
                                        private static BasicTextEncryptor basicTextEncryptor = new BasicTextEncryptor();
                                        static {
                                            basicTextEncryptor.setPassword(PRIVATE_KEY);
                                        }
                                        /**
                                         * 私有构造方法,防止被意外实例化
                                         */
                                        private JasyptUtils() {
                                        }
                                        /**
                                         * 明文加密
                                         *
                                         * @param plaintext 明文
                                         * @return String
                                         */
                                        public static String encrypt(String plaintext) {
                                            log.info("明文字符串为:{}", plaintext);
                                            // 使用的加密算法参考2.2节内容,也可以在源码的类注释中看到
                                            String ciphertext = basicTextEncryptor.encrypt(plaintext);
                                            log.info("密文字符串为:{}", ciphertext);
                                            return ciphertext;
                                        }
                                        /**
                                         * 解密
                                         *
                                         * @param ciphertext 密文
                                         * @return String
                                         */
                                        public static String decrypt(String ciphertext) {
                                            log.info("密文字符串为:{}", ciphertext);
                                            ciphertext = "ENC(" + ciphertext + ")";
                                            if (PropertyValueEncryptionUtils.isEncryptedValue(ciphertext)) {
                                                String plaintext = PropertyValueEncryptionUtils.decrypt(ciphertext, basicTextEncryptor);
                                                log.info("明文字符串为:{}", plaintext);
                                                return plaintext;
                                            }
                                            log.error("解密失败!");
                                            return "";
                                        }
                                    }
                                    
                                    @Slf4j
                                    public class JasyptUtilsTest {
                                        @Test
                                        public void testEncrypt(){
                                            String plainText = "Glrs@1234";
                                            String ciperText = JasyptUtils.encrypt(plainText);
                                            log.info("加密后的密文为:{}", ciperText);
                                        }
                                        @Test
                                        public void testDecrypt(){
                                            String ciperText = "KZeGx0ixuy4UrBp1HuhiDNnKB0cJr0cW";
                                            String plainText = JasyptUtils.decrypt(ciperText);
                                            log.info("解密后的明文为:{}", plainText);
                                        }
                                    }
                                    

                                    经过如上的配置,启动项目,如下从数据库获取数据的应用逻辑就能正常使用了。

                                    @Slf4j
                                    @RestController
                                    public class HelloController {
                                        @Autowired
                                        UserMapper userMapper;
                                        @GetMapping("/getHello")
                                        public String getHello(){
                                            log.info("myapp works!");
                                            List users = userMapper.listUsers();
                                            return users.toString();
                                        }
                                    }
                                    
                                    @Mapper
                                    public interface UserMapper {
                                        List listUsers();
                                    }
                                    
                                    
                                        
                                    
                                    

                                    四、生成环境启动

                                    生产环境密钥作为启动参数:

                                    java -jar -Djasypt.encryptor.password=your-secret
                                    

                                    无、参考文档

                                    Jasypt: Java simplified encryption - Jasypt: Java simplified encryption - Main