Springboot AOP对指定敏感字段数据加密存储的实现

网友投稿 901 2022-11-21

Springboot AOP对指定敏感字段数据加密存储的实现

Springboot AOP对指定敏感字段数据加密存储的实现

前言

本文主要内容:

1. 插入数据 自定义注解方式  对 指定接口方法 的 参数的指定字段进行 加密存储;

2.对数据内的加密数据,进行解密返回

先看看效果 :

数据存入数据库表内, 手机号phone和邮箱email 属于敏感数据,我们需要密文存储 :

查询解密返回:

1.  自定义注解 加密标识注解  NeedEncrypt.java :

import java.lang.annotation.*;

/**

* @Author JCccc

* @Description 需加密

* @Date 2021/7/23 11:55

*/

@Target({ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

public @interface NeedEncrypt {

}

2.自定义注解 需加密字段标识注解 EncryptField.java :

/**

* @Author JCccc

* @Description

* @Date 2021/7/23 11:55

*/

@Target({ElementType.FIELD,ElementType.PARAMETER})

@Retention(RetentionPolicy.RUNTIME)

public @interface EncryptField {

String[] value() default "";

}

3.加密逻辑的aop处理器  EncryptAspect.java :

import com.elegant.dotest.aop.annotation.EncryptField;

import lombok.extern.slf4j.Slf4j;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Pointcut;

import org.jasypt.encryption.StringEncryptor;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Component;

import java.lang.reflect.Field;

import java.util.Objects;

/**

* @Author JCccc

* @Description

* @Date 2021/9/14 8:55

*/

@Slf4j

@Aspect

@Component

public class EncryptAspect {

@Autowired

private StringEncryptor stringEncryptor;

@Pointcut("@annotation(com.elegant.dotest.aop.annotation.NeedEncrypt)")

public void pointCut() {

}

@Around("pointCut()")

public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

//加密

encrypt(joinPoint);

return joinPoint.proceed();

}

public void encrypt(ProceedingJoinPoint joinPoint) {

Object[] objects=null;

try {

objects = joinPoint.getArgs();

if (objects.length != 0) {

for (int i = 0; i < objects.length; i++) {

//抛砖引玉 ,可自行扩展其他类型字段的判断

if (objects[i] instanceof String) {

objects[i] = encryptValue(objects[i]);

} else {

encryptObject(objects[i]);

}

}

}

} catch (Exception e) {

e.printStackTrace();

}

}

/**

* 加密对象

* @param obj

* @throws IllegalAccessException

*/

private void encryptObject(Object obj) throws IllegalAccessException {

if (Objects.isNull(obj)) {

log.info("当前需要加密的object为null");

return;

}

Field[] fields = obj.getClass().getDeclaredFields();

for (Field field : fields) {

boolean containEncryptField = field.isAnnotationPresent(EncryptField.class);

if (containEncryptField) {

//获取访问权

field.setAccessible(true);

String value = stringEncryptor.encrypt(String.valueOf(field.get(obj)));

field.set(obj, value);

}

}

}

/**

* 加密单个值

* @param realValue

* @return

*/

public String encryptValue(Object realValue) {

try {

realValue = stringEncryptor.encrypt(String.valueOf(realValue));

} catch (Exception e) {

log.info("加密异常={}",e.getMessage());

}

return String.valueOf(realValue);

}

}

4. 插入user表 使用的 User.java :

import com.elegant.dotest.aop.annotation.EncryptField;

import lombok.Data;

import lombok.experimental.Accessors;

/**

* @Author JCccc

* @Description

* @Date 2021/9/14 8:55

*/

@Data

@Accessors(chain = true)

public class User {

privatjsCrRwIlNKe Integer id;

private String name;

@EncryptField

private String phone;

@EncryptField

private String email;

private Integer age;

}

可以看到,手机号phone 和 邮箱 email 两个字段,我们做了注解 @EncryptField 标识:

ok,我们写个测试接口,使用 @NeedEncrypt 注解标识这个接口需要进行加密拦截 :

使用postman调用一下测试接口:

可以看下数据库,数据已经加密存储成功:

接下来是查询解密环节:

解密这里其实有些小讲究。 因为查询出来的数据有可能是单个实体,也可能是List (其实甚至是Map或者Set,又或者是 分页数据类)

所以本文将会以 最常用的 单个实体 、 List<实体> 为例子,去做解密。

1.解密自定义注解 NeedDecrypt.java :

/**

* @Author JCccc

* @Description 需解密

* @Date 2021/7/23 11:55

*/

@Target({ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

public @interface NeedDecrypt {

}

2. 解密逻辑的aop处理器  DecryptAspect.java :

import com.elegant.dotest.aop.annotation.EncryptField;

import lombok.extern.slf4j.Slf4j;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Pointcut;

import org.jasypt.encryption.StringEncryptor;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Component;

import java.lang.reflect.Field;

import java.util.ArrayList;

import java.util.Collections;

import java.util.List;

import java.util.Objects;

/**

* @Author JCccc

* @Description

* @Date 2021/9/14 8:55

*/

@Slf4j

@Aspect

@Component

public class DecryptAspect {

@Autowired

private StringEncryptor stringEncryptor;

@Pointcut("@annotation(com.elegant.dotest.aop.annotation.NeedDecrypt)")

public void pointCut() {

}

@Around("pointCut()")

public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

//解密

Object result = decrypt(joinPoint);

return result;

}

public Object decrypt(ProceedingJoinPoint joinPoint) {

Object result = null;

try {

Object obj = joinPoint.proceed();

if (obj != null) {

//抛砖引玉 ,可自行扩展其他类型字段的判断

if (obj instanceof String) {

decryptValue(obj);

http:// } else {

result = decryptData(obj);

}

}

} catch (Throwable e) {

e.printStackTrace();

}

return result;

}

private Object decryptData(Object obj) throws IllegalAccessException {

if (Objects.isNull(obj)) {

return null;

}

if (obj instanceof ArrayList) {

decryptList(obj);

} else {

decryptObj(obj);

}

return obj;

}

/**

* 针对单个实体类进行 解密

* @param obj

* @throws IllegalAccessException

*/

private void decryptObj(Object obj) throws IllegalAccessException {

Field[] fields = obj.getClass().getDeclaredFields();

for (Field field : fields) {

boolean hasSecureField = field.isAnnotationPresent(EncryptField.class);

if (hasSecureField) {

field.setAccessible(true);

String realValue = (String) field.get(obj);

String value = stringEncryptor.decrypt(realValue);

field.set(obj, value);

}

}

}

/**

* 针对list<实体来> 进行反射、解密

* @param obj

* @throws IllegalAccessException

*/

private void decryptList(Object obj) throws IllegalAccessException {

List result = new ArrayList<>();

if (obj instanceof ArrayList) {

for (Object o : (List>) obj) {

result.add(o);

}

}

for (Object object : result) {

decryptObj(object);

}

}

public String decryptValue(Object realValue) {

try {

realValue = stringEncryptor.encrypt(String.valueOf(realValue));

} catch (Exception e) {

log.info("解密异常={}", e.getMessage());

}

return String.valueOf(realValue);

}

}

然后我们对一个查询方法进行测试 :

我们先试一下查询单条数据的:

使用@NeedDecrypt注解标记这个接口需要数据解密:

调用接口看看结果:

然后是多条数据List 返回的接口:

调用接口测试看看结果:

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:SQL面试题-直播APP统计PV&UV
下一篇:泛型_泛型方法_可变参数
相关文章

 发表评论

暂时没有评论,来抢沙发吧~