[SpringBoot Advanced] SpringBoot integrates jasypt configuration desensitization and data desensitization

Configuration desensitization

scenes to be used

The database password is directly written in the application.yml configuration in clear text, which is a big challenge for security. Once the password is leaked, it will bring great security risks. Especially in some enterprises with high security requirements, we consider how to encrypt passwords.

The open source framework Jasypt can solve the above problems.

  • The Jasypt open source security framework is specially designed to handle Spring boot attribute encryption. Use a specific format to directly configure the ciphertext in the configuration file, and then when the application starts, Jasypt will automatically decrypt the password into plaintext for use by the program.

  • jasypt encrypts the same content with the same key (secretKey), and the ciphertext generated each time is different, but it is possible to decrypt the original content according to these ciphertexts.

Configuration Masking Practice

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

Add the key configuration item to the configuration file jasypt.encryptor.password, and replace the value that needs to be desensitized valuewith the pre-encrypted content ENC(zxcvb/asdfg).

We can define this format at will. For example, if you want abc[zxcvb/asdfg]the format, you only need to configure the prefix and suffix.

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

ENC(XXX)jasyptThe format is mainly for the convenience of identifying whether the value needs to be decrypted. If it is not configured according to this format, the original value will be kept when the configuration item is loaded and will not be decrypted.

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

A pre-generated encrypted value can be generated by calling the API in the code

@Autowired
private StringEncryptor stringEncryptor;

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

data desensitization

The private data of users in the production environment, such as mobile phone numbers, ID cards, or some account configuration information, must be desensitized without landing when entering the warehouse, that is, it must be desensitized in real time when entering our system.

User data enters the system, is desensitized and persisted to the database, and reverse decryption is performed when the user queries the data. This kind of scene generally requires global processing, so it AOPis not suitable to use aspects to realize it.

First, customize two annotations @EncryptFieldand @EncryptMethoduse them on field properties and methods respectively. The implementation idea is very simple. As long as the @EncryptMethodannotations are applied to the method, check whether the input field is marked with @EncryptFieldannotations, and if so, encrypt the corresponding field content.

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;
    }
}

insert image description here

Guess you like

Origin blog.csdn.net/qq_45297578/article/details/129367804