微服务SpringBoot整合Jasypt加密工具_springboot jasypt-程序员宅基地

技术标签: 微服务  spring boot  java  加密算法  加密工具  

在这里插入图片描述

一、Jasypt介绍

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

引入依赖如下:

<!-- https://mvnrepository.com/artifact/com.github.ulisesbocchio/jasypt-spring-boot-starter -->
<dependency>
    <groupId>com.github.ulisesbocchio</groupId>
    <artifactId>jasypt-spring-boot-starter</artifactId>
    <version>3.0.4</version>
</dependency>

二、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<User> users = userMapper.listUsers();
        return users.toString();
    }

}
@Mapper
public interface UserMapper {
    
    List<User> listUsers();
}
<mapper namespace="com.example.myapp.mapper.UserMapper">
    <select id="listUsers" resultType="com.example.myapp.bean.User">
        select zu.user_id userId, zu.user_name userName, zu.age age, zu.user_email userEmail from zx_user zu;
    </select>
</mapper>

四、生成环境启动

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

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

无、参考文档

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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_42469135/article/details/127265988

智能推荐

常用的投影图像编码方法之时间多路编码方法_多路分时编码-程序员宅基地

文章浏览阅读295次。在1998年,彩色编码方法被提出,该方法利用颜色的多级格雷码方法,使用n个符号字母表,使每个字母和一个特定的RGB模型中的一个颜色对应,因此,二值码的推广是有效的,当投射a幅编码图像时,二值码既可以确定出。格雷码编码出的码值在前后位之间最多只会有一位不相同,因此,该方法极大地解决二进制码在黑白边缘附近解码时会出现错误的情况,提高了编码的稳定性、鲁棒性,并且对于格雷码自身而言,它是循环码因此具有自补性,格雷码的优点均使其在编码解码过程中降低误差的出现。二值编码、多值编码、时间编码结合相移编码和混合编码。_多路分时编码

js金钱千分位分隔符_js money 千分号-程序员宅基地

文章浏览阅读499次。金钱千分位分隔符:function moneyFormat(nMoney){ if(nMoney == 0){ return ‘0.00’; } if(nMoney && nMoney != null){ nMoney = String(nMoney); var sLeft = nMoney.split(’.’)[0], sRight = nMoney.split(’.’)[1]; sRight = sRight ? (sRig_js money 千分号

APM2323AAC-TRL-VB一款SOT23封装P—Channel场效应MOS管-程序员宅基地

文章浏览阅读312次,点赞8次,收藏7次。*总结:** APM2323AAC-TRL-VB是一款适用于负电压场景的P-Channel沟道MOSFET,包括负电压电源开关、负电压电池保护、负载开关和逆变器等领域。1. **负电压电源开关:** APM2323AAC-TRL-VB适用于负电压电源开关模块,特别在需要进行负电压电源管理的应用场景。3. **负载开关:** 在需要对负载进行高效控制的领域,如便携式设备或负电压负载开关模块,APM2323AAC-TRL-VB可用于负载开关,提供可靠的电流调节和保护功能。**封装:** SOT23。

2024/3/8洛谷刷题(模拟与高精度)-程序员宅基地

文章浏览阅读353次,点赞4次,收藏7次。先特判1,如果是1不输出,但是是-1的话要输出-号,所以就是(n-&&i)但是-1在常数项的时候可以出现,所以其实我们就知道了,只要是最后一项,除了0都可以输出, 而且其他系数除一外也是直接输出所以就是(abs(n)>1||i==0)1.第一考虑的肯定就是+或-号的输出啦,负数自带符号,所以只用考虑+号的输出,不用输出的有第一项和非正数(0的话也不用),0的话整项不见,第一项和负数的话我们直接输出,所以条件为(i!2.如何接下来就是考虑系数的输出了,系数特判1和-1,遇到这种一般会用到abs来解决。

python按指定概率抽样_python:抽样和抽样方法-程序员宅基地

文章浏览阅读2.4k次。学习目标目标知道总体、样本、样本大小、样本数量知道样本统计量和总体统计量知道总体分布、样本分布和抽样分布知道常用的抽样方法某糖果公司研发了一种超长效口香糖,为了得到口味持续时间的数据,公司聘请了试吃者帮忙完成检验,结果却让人大跌眼镜!没文化,真可怕!我该怎么办? 有时候数据很容易收集,例如参加健身俱乐部的人的年龄,后这一家游戏公司的销售数据。但有时候不太容易,该怎么办呢? 是时候拿出终极武器了— ..._以某种概率采样

nrf51822 nrfjprog.exe ERROR: Invalid serial number!_nrfjprog couldn't be executed-程序员宅基地

文章浏览阅读1.1k次。遇到问题: Hi,I use nRFgo Studio to erase nRF51822, like this but it display: ERROR: Invalid serial number! Is this a J-link? I’ve never seen a J-Link with a 10 digit serial number, but maybe I’m w..._nrfjprog couldn't be executed

随便推点

GridView控件属性及应用(转载)-程序员宅基地

文章浏览阅读645次。1.GridView控件的属性GridView支持大量属性,这些属性属于如下几大类:行为、可视化设置、样式、状态和模板。行为属性描述AllowPaging指示该控件是否支持分页。AllowSorting指示该控件是否支持排序。AutoGenerateColumn..._avicvxegrid 组件那个属性是存的是数据

ubuntu 16.04下安装docker后,执行docker出现权限不足的解决办法_linux docker: you are not authorized to perform th-程序员宅基地

文章浏览阅读1.7k次。ubuntu 16.04下安装docker,具体可见下面的连接:https://blog.csdn.net/jinking01/article/details/82490688使用sudo安装docker完成后,普通用户执行docker ps,报错connect: permission denied,链接权限被拒绝。具体解决办法为:1.添加当前用户到docker 用户组sudo gpasswd -a ${USER} docker2.查看用户组下用户,检查添加是否成功cat /etc/grou_linux docker: you are not authorized to perform this operation: server retur

离群值是什么意思_学术必备!代谢组学及数据分析相关问题汇总-程序员宅基地

文章浏览阅读2.2k次。为方便大家快速地掌握代谢组学及数据分析相关知识,现把咨询我们的有关代谢组学及数据分析的一些问题给大家整理出来,供大家参考。1.PCA:loading图,P=COSα中P代表什么意思?The loading, p, for a selected PCA dimension, represent the importance of the X variables in that dimension。2..._代谢组学pca有一组样品离散

STM32·HAL库开发(十八)不同芯片间程序的移植——案例:STM32F103C8T6程序移植到STM32F103RCT6_基于hal库的stm32ct86和stm32rct6能移植嘛-程序员宅基地

文章浏览阅读733次。不同芯片间程序的移植——案例:STM32F103C8T6程序移植到STM32F103RCT6_基于hal库的stm32ct86和stm32rct6能移植嘛

JavaScript数组和字符串的方法总结_javascript 字符串数组-程序员宅基地

文章浏览阅读1k次,点赞13次,收藏13次。参数为回调函数,会遍历数组所有的项,回调函数接受三个参数,分别为value,index,self;反向归井,同forEach,迭代数组的所有项,并构建一个最终值,由reduceRight返回。同forEach,同时回调函数返回布尔值,为true的数据组成新数组由filter返回。同forEach,同时回调函数返回布尔值,只要由一个为true,由some返回true。归并,同forEach,迭代数组的所有项,并构建一个最终值,由reduce返回。提取字符串的片断,并在新的字符串中返回被提取的部分。_javascript 字符串数组

新浪实时股票数据接口http://hq.sinajs.cn/list=code_hq.sinajs.cn、-程序员宅基地

文章浏览阅读6.9w次,点赞17次,收藏59次。股票数据的获取目前有如下两种方法可以获取:1.http/javascript接口取数据2. web-service接口1.http/javascript接口取数据1.1Sina股票数据接口以大秦铁路(股票代码:601006)为例,如果要获取它的最新行情,只需访问新浪的股票数据接口:http://hq.sinajs.cn/list=sh601006这个url会返回一串_hq.sinajs.cn、