【SpringBoot高级篇】SpringBoot集成jasypt 配置脱敏和数据脱敏

news/2024/7/7 19:48:06

【SpringBoot高级篇】SpringBoot集成jasypt数据脱敏

  • 配置脱敏
    • 使用场景
    • 配置脱敏实践
  • 数据脱敏
    • pom
    • yml
    • EncryptMethod
    • EncryptField
    • EncryptConstant
    • EncryptHandler
    • Person
    • JasyptApplication

配置脱敏

使用场景

数据库密码直接明文写在application.yml配置中,对安全来说,是一个很大的挑战。一旦密码泄漏,将会带来很大的安全隐患。尤其在一些企业对安全性要求很高,因此我们就考虑如何对密码进行加密。

开源框架Jasypt可以解决上面的问题。

  • Jasypt 开源安全框架就是专门用于处理 Spring boot 属性加密的,在配置文件中使用特定格式直接配置密文,然后应用启动的时候,Jasypt 会自动将密码解密成明文供程序使用。

  • jasypt 同一个密钥(secretKey)对同一个内容执行加密,每次生成的密文都是不一样的,但是根据根据这些密文解密成原内容都是可以的。

配置脱敏实践

<!--配置文件加密-->
 <dependency>
     <groupId>com.github.ulisesbocchio</groupId>
     <artifactId>jasypt-spring-boot-starter</artifactId>
     <version>2.1.0</version>
 </dependency>

配置文件加入秘钥配置项jasypt.encryptor.password,并将需要脱敏的value值替换成预先经过加密的内容ENC(zxcvb/asdfg)

这个格式我们是可以随意定义的,比如想要abc[zxcvb/asdfg]格式,只要配置前缀和后缀即可。

jasypt:
  encryptor:
    property:
      prefix: "abc["
      suffix: "]"

ENC(XXX)格式主要为了便于识别该值是否需要解密,如不按照该格式配置,在加载配置项的时候jasypt将保持原值,不进行解密。

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://localhost:3306/order_db_1?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
    username: root
    password: ENC(ipHBo9hH/W756iU3HjAZJA==)
# 秘钥
jasypt:
  encryptor:
    password: csdn

预先生成的加密值,可以通过代码内调用API生成

@Autowired
private StringEncryptor stringEncryptor;

@GetMapping("/encode")
public String encrypt(String content) {
    String encryptStr = stringEncryptor.encrypt(content);
    System.out.println("加密后的内容:" + encryptStr);
    return "加密后的内容:" + encryptStr;
}

数据脱敏

生产环境用户的隐私数据,比如手机号、身份证或者一些账号配置等信息,入库时都要进行不落地脱敏,也就是在进入我们系统时就要实时的脱敏处理。

用户数据进入系统,脱敏处理后持久化到数据库,用户查询数据时还要进行反向解密。这种场景一般需要全局处理,那么用AOP切面来实现在适合不过了。

首先自定义两个注解@EncryptField@EncryptMethod分别用在字段属性和方法上,实现思路很简单,只要方法上应用到@EncryptMethod注解,则检查入参字段是否标注@EncryptField注解,有则将对应字段内容加密。

pom

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>

    <dependency>
        <groupId>com.github.ulisesbocchio</groupId>
        <artifactId>jasypt-spring-boot-starter</artifactId>
        <version>2.1.0</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>

yml

# 秘钥
jasypt:
  encryptor:
    password: csdn

EncryptMethod

@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptMethod {

    String type() default EncryptConstant.ENCRYPT;
}

EncryptField

@Documented
@Target({ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptField {

    String[] value() default "";
}

EncryptConstant

public interface EncryptConstant {
    // 加密
    String ENCRYPT = "encrypt";

    // 解密
    String DECRYPT = "decrypt";
}

EncryptHandler

@Slf4j
@Aspect
@Component
public class EncryptHandler {

    @Autowired
    private StringEncryptor stringEncryptor;

    @Pointcut("@annotation(cn.zysheep.annotation.EncryptMethod)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) {
        /**
         * 加密
         */
        Object[] encrypt = encrypt(joinPoint);
        /**
         * 解密
         */
        Object decrypt = decrypt(joinPoint,encrypt);
        return decrypt;
    }

    public Object[] encrypt(ProceedingJoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        try {
            if (args.length != 0) {
                for (int i = 0; i < args.length; i++) {
                    Object o = args[i];
                    if (o instanceof String) {
                        args[i] = encryptValue(o);
                    } else {
                        args[i] = handler(o, EncryptConstant.ENCRYPT);
                    }
                    //TODO 其余类型自己看实际情况加
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return args;
    }

    public Object decrypt(ProceedingJoinPoint joinPoint,Object[] args) {
        Object result = null;
        try {
            Object obj = joinPoint.proceed(args);
            if (obj != null) {
                if (obj instanceof String) {
                    result = decryptValue(obj);
                } else {
                    result = handler(obj, EncryptConstant.DECRYPT);
                }
                //TODO 其余类型自己看实际情况加
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return result;
    }

    private Object handler(Object obj, String type) throws IllegalAccessException {

        if (Objects.isNull(obj)) {
            return null;
        }
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            boolean annotationPresent = field.isAnnotationPresent(EncryptField.class);
            if (annotationPresent) {
                field.setAccessible(true);
                String value;
                String realValue = (String) field.get(obj);
                if (EncryptConstant.DECRYPT.equals(type)) {
                    value = stringEncryptor.decrypt(realValue);
                } else {
                    value = stringEncryptor.encrypt(realValue);
                }
                field.set(obj, value);
            }
        }
        return obj;
    }

    public String encryptValue(Object realValue) {
        String value = null;
        try {
            value = stringEncryptor.encrypt(String.valueOf(realValue));
        } catch (Exception ex) {
            return value;
        }
        return value;
    }

    public String decryptValue(Object realValue) {
        String value = String.valueOf(realValue);
        try {
            value = stringEncryptor.decrypt(value);
        } catch (Exception ex) {
            return value;
        }
        return value;
    }
}

Person

@Data
public class Person {

    private Integer id;

    @EncryptField
    private String mobile;

    @EncryptField
    private String address;

    private Integer age;

    private BigDecimal wages;
}

JasyptApplication

@RestController
@SpringBootApplication
public class JasyptApplication {

    @Autowired
    private StringEncryptor stringEncryptor;

    public static void main(String[] args) {
        SpringApplication.run(JasyptApplication.class, args);
    }

    @GetMapping("/encode")
    public String encrypt(String content) {
        String encryptStr = stringEncryptor.encrypt(content);
        System.out.println("加密后的内容:" + encryptStr);
        return "加密后的内容:" + encryptStr;
    }

    @EncryptMethod
    @PostMapping("/dataEnc")
    public Person encrypt(@RequestBody Person person, @EncryptField String username) throws JsonProcessingException {
        ObjectMapper json = new ObjectMapper();
        String writeValueAsString = json.writeValueAsString(person);

        System.out.println(writeValueAsString);

        System.out.println(username);
        return person;
    }


    @EncryptMethod
    @GetMapping("/getParam")
    public String getParam( @EncryptField String username) {
        System.out.println("保存数据库业务操作===>username: "+username);
        return username;
    }
}

在这里插入图片描述


http://lihuaxi.xjx100.cn/news/965883.html

相关文章

HTTP的强缓存与协商缓存

强缓存策略 直接从本地副本比对读取&#xff0c;不去请求服务器&#xff0c;返回的状态码是 200。 cache-control HTTP1.1 新增了 cache-control 字段&#xff0c;当 cache-control 和 expires 都存在时&#xff0c;cache-control 优先级更高。该字段是一个时间长度&#xf…

完美解决Column ‘xxx‘ in field list is ambiguous问题

复现问题 使用如下SQL查询数据&#xff1a; SELECTid as id,user.login_name AS user_mobile,apply_status,( SELECT value FROM data_dict WHERE CODE apply_status ) AS apply_status_value,apply_no,application_name,belong_org_code,belong_org_data_dict.value AS bel…

交叉编译和 RPC

本篇文章译自英文文档 Cross Compilation and RPC 作者是 Ziheng Jiang&#xff0c;Lianmin Zheng。更多 TVM 中文文档可访问 →TVM 中文站 本教程介绍了如何在 TVM 中使用 RPC 进行交叉编译和远程设备执行。 利用交叉编译和 RPC&#xff0c;可以实现程序在本地机器编译&…

scikit-image:遥感图像geotiff格式转mat格式

scikit-imagescikit-image 是一个专门用于图像处理的 Python 库&#xff0c;它可以与 Scipy 库和其他可能有助于计算的 Python 库一起使用。Github地址&#xff1a;https://github.com/scikit-image/scikit-image Star有5.3k首先pip安装scikit-image包&#xff0c;或者直接使用…

中文代码86

PK 嘚釦 docProps/PK 嘚釦諿A眎 { docProps/app.xml漅薾?糤?D?v拢W4揣狤"攃e9 睔貣m*:PAz韒g?项弇}R珁湧4嶱 ]I禑菦?櫮戵\U佳 珩 ]铒e礎??X(7弅锿?jl筀儸偛佣??z窊梈ZT炰攷 ?\ 銒沆?状尧绥>蕮 ?斬殕{do]?o乗YX?:??罢秗,泿)怟 …

基于JSP的网上书城

技术&#xff1a;Java、JSP等摘要&#xff1a;随着科技的迅速发展&#xff0c;计算机技术已应用到社会的各个领域。随着计算机技术和通信技术的迅速发展&#xff0c;网络的规模也逐渐增大&#xff0c;网络的元素也随之不断增加&#xff0c;有的利用其通信&#xff0c;有的利用其…

2023年总结的web前端学习路线分享(学习导读)

如果你打开了这篇文章&#xff0c;说明你是有兴趣想了解前端的这个行业的&#xff0c;以下是博主2023年总结的一些web前端的学习分享路线&#xff0c;如果你也想从事前端或者有这方面的想法的&#xff0c;请接着往下看&#xff01; 前端发展前景 前端入门 巩固基础 前端工程…

Java线程池的七个参数

一、前言 1.1 线程池是什么 线程池&#xff08;thread pool&#xff09;&#xff1a;线程池是一种池化技术&#xff0c;类似的有数据库连接池&#xff0c;HTTP连接池、IP池等。池化管理线程&#xff0c;无需额外创建和销毁线程&#xff0c;能避免创建过多线程导致线程频繁调度…