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ò)展其他類型字段的判斷 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) { //獲取訪問權(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,我們寫個(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ù)類)
所以本文將會(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ò)展其他類型字段的判斷 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í)體類進(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)文章
ReentrantReadWriteLock不能鎖升級(jí)的原因總結(jié)
今天給大家?guī)?lái)的是關(guān)于Java并發(fā)的相關(guān)知識(shí),文章圍繞著為什么ReentrantReadWriteLock不能鎖升級(jí)展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-06-06Spring Boot啟動(dòng)過程(五)之Springboot內(nèi)嵌Tomcat對(duì)象的start教程詳解
這篇文章主要介紹了Spring Boot啟動(dòng)過程(五)之Springboot內(nèi)嵌Tomcat對(duì)象的start的相關(guān)資料,需要的朋友可以參考下2017-04-04SpringCloud解決Feign異步回調(diào)問題(SpringBoot+Async+Future實(shí)現(xiàn))
這篇文章主要介紹了SpringCloud解決Feign異步回調(diào)問題(SpringBoot+Async+Future實(shí)現(xiàn)),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11利用SpringDataJPA開啟審計(jì)功能,自動(dòng)保存操作人操作時(shí)間
這篇文章主要介紹了利用SpringDataJPA開啟審計(jì)功能,自動(dòng)保存操作人操作時(shí)間,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12