Springboot AOP對(duì)指定敏感字段數(shù)據(jù)加密存儲(chǔ)的實(shí)現(xiàn)
前言
本文主要內(nèi)容:
1. 插入數(shù)據(jù) 自定義注解方式 對(duì) 指定接口方法 的 參數(shù)的指定字段進(jìn)行 加密存儲(chǔ);
2.對(duì)數(shù)據(jù)內(nèi)的加密數(shù)據(jù),進(jìn)行解密返回
先看看效果 :
數(shù)據(jù)存入數(shù)據(jù)庫(kù)表內(nèi), 手機(jī)號(hào)phone和郵箱email 屬于敏感數(shù)據(jù),我們需要密文存儲(chǔ) :

查詢解密返回:

1. 自定義注解 加密標(biāo)識(shí)注解 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.自定義注解 需加密字段標(biāo)識(shí)注解 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++) {
//拋磚引玉 ,可自行擴(kuò)展其他類(lèi)型字段的判斷
if (objects[i] instanceof String) {
objects[i] = encryptValue(objects[i]);
} else {
encryptObject(objects[i]);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 加密對(duì)象
* @param obj
* @throws IllegalAccessException
*/
private void encryptObject(Object obj) throws IllegalAccessException {
if (Objects.isNull(obj)) {
log.info("當(dāng)前需要加密的object為null");
return;
}
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
boolean containEncryptField = field.isAnnotationPresent(EncryptField.class);
if (containEncryptField) {
//獲取訪問(wèn)權(quán)
field.setAccessible(true);
String value = stringEncryptor.encrypt(String.valueOf(field.get(obj)));
field.set(obj, value);
}
}
}
/**
* 加密單個(gè)值
* @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 {
private Integer id;
private String name;
@EncryptField
private String phone;
@EncryptField
private String email;
private Integer age;
}
可以看到,手機(jī)號(hào)phone 和 郵箱 email 兩個(gè)字段,我們做了注解 @EncryptField 標(biāo)識(shí):

ok,我們寫(xiě)個(gè)測(cè)試接口,使用 @NeedEncrypt 注解標(biāo)識(shí)這個(gè)接口需要進(jìn)行加密攔截 :

使用postman調(diào)用一下測(cè)試接口:

可以看下數(shù)據(jù)庫(kù),數(shù)據(jù)已經(jīng)加密存儲(chǔ)成功:

接下來(lái)是查詢解密環(huán)節(jié):
解密這里其實(shí)有些小講究。 因?yàn)椴樵兂鰜?lái)的數(shù)據(jù)有可能是單個(gè)實(shí)體,也可能是List (其實(shí)甚至是Map或者Set,又或者是 分頁(yè)數(shù)據(jù)類(lèi))
所以本文將會(huì)以 最常用的 單個(gè)實(shí)體 、 List<實(shí)體> 為例子,去做解密。
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) {
//拋磚引玉 ,可自行擴(kuò)展其他類(lèi)型字段的判斷
if (obj instanceof String) {
decryptValue(obj);
} 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;
}
/**
* 針對(duì)單個(gè)實(shí)體類(lèi)進(jìn)行 解密
* @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);
}
}
}
/**
* 針對(duì)list<實(shí)體來(lái)> 進(jìn)行反射、解密
* @param obj
* @throws IllegalAccessException
*/
private void decryptList(Object obj) throws IllegalAccessException {
List<Object> 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);
}
}
然后我們對(duì)一個(gè)查詢方法進(jìn)行測(cè)試 :

我們先試一下查詢單條數(shù)據(jù)的:
使用@NeedDecrypt注解標(biāo)記這個(gè)接口需要數(shù)據(jù)解密:

調(diào)用接口看看結(jié)果:

然后是多條數(shù)據(jù)List<User> 返回的接口:

調(diào)用接口測(cè)試看看結(jié)果:

到此這篇關(guān)于Springboot AOP對(duì)指定敏感字段數(shù)據(jù)加密存儲(chǔ)的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)數(shù)據(jù)加密存儲(chǔ)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IDEA運(yùn)行SpringBoot項(xiàng)目的超詳細(xì)步驟截圖
在當(dāng)前的開(kāi)發(fā)中Spring Boot開(kāi)發(fā)框架已經(jīng)成為主流,下面這篇文章主要給大家介紹了關(guān)于IDEA運(yùn)行SpringBoot項(xiàng)目的超詳細(xì)步驟截圖,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2022-11-11
WebsocketClient斷線的一些常見(jiàn)原因及解決辦法
這篇文章主要給大家介紹了關(guān)于WebsocketClient斷線的一些常見(jiàn)原因及解決辦法,文中通過(guò)代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用WebsocketClient斷線具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-12-12
使用InputStream的available()能否用來(lái)判斷當(dāng)前流是否讀取到文件
這篇文章主要介紹了使用InputStream的available()能否用來(lái)判斷當(dāng)前流是否讀取到文件問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06
JPA findById方法和getOne方法的區(qū)別說(shuō)明
這篇文章主要介紹了JPA findById方法和getOne方法的區(qū)別,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。2021-08-08
java中子類(lèi)繼承父類(lèi),程序運(yùn)行順序的深入分析
本篇文章是對(duì)java中子類(lèi)繼承父類(lèi),程序運(yùn)行順序進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06
SpringBoot處理大量請(qǐng)求數(shù)據(jù)的傳輸問(wèn)題的方法小結(jié)
在Spring?Boot項(xiàng)目常常需要中處理大量請(qǐng)求數(shù)據(jù)的傳輸問(wèn)題,這篇文章主要為大家整理了一些常用的方法,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-01-01
Java中Arraylist動(dòng)態(tài)擴(kuò)容方法詳解
ArrayList的列表對(duì)象實(shí)質(zhì)上是存儲(chǔ)在一個(gè)引用型數(shù)組里的,下面這篇文章主要給大家介紹了關(guān)于Java中Arraylist動(dòng)態(tài)擴(kuò)容方法的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-08-08
java外賣(mài)訂餐系統(tǒng)小項(xiàng)目
這篇文章主要為大家詳細(xì)介紹了java外賣(mài)訂餐系統(tǒng)小項(xiàng)目,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01

