序言:
在開發(fā)過(guò)程中因?yàn)楣δ艿奶厥庑? 需要對(duì)客戶信息的手機(jī)號(hào)進(jìn)行加密處理 然后存到數(shù)據(jù)庫(kù)中,然而這個(gè)需求是項(xiàng)目中期加入,很多功能上已經(jīng)使用了獲取客戶信息的方法,沒法統(tǒng)一控制手機(jī)號(hào)加解密操作, 于是考慮使用 aop做環(huán)繞增強(qiáng) 對(duì)所有出參進(jìn)行,解密操作(這里只對(duì))。
Spring AOP--@Around--使用/實(shí)例 使用或理解上的問(wèn)題
tips: 這里不贅述 注解的含義等基本概念,是以使用@Around為主的新手教程
需要引入jar包
<!-- #Spring中的切面依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
對(duì)于aop 網(wǎng)上有很多的代碼示例,但是有些看不明白、寫得比較含糊的問(wèn)題列一下
問(wèn)題1 @Around中的方法名稱怎么定義:
@Around("test()")? 這個(gè)中的? test()方法是自己定義的切面的方法 必須要通過(guò)@Pointcut指定切面才能生效
// 指定全局的切面
@Pointcut("execution (* com.dealer.api.controller.*.*(..))")
public void test() {
}
// 環(huán)繞增強(qiáng)
@Around("test()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) {
Object proceed = null;
String className = proceedingJoinPoint.getSignature().getDeclaringTypeName();
String methodName = proceedingJoinPoint.getSignature().getName();
System.out.println("============== " + className + " 類中的方法:" + methodName + " 開始執(zhí)行 ==============");
MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
// 參數(shù)名稱
String[] parameterNames = methodSignature.getParameterNames();
// 參數(shù)值
Object[] args = proceedingJoinPoint.getArgs();
for (int i = 0; i < args.length; i++) {
System.out.println("第" + i + "個(gè)參數(shù):" + " key:" + parameterNames[i] + " value:" + args[i]);
}
try {
proceed = proceedingJoinPoint.proceed();
// ====================== 返回==============================
System.out.println("執(zhí)行結(jié)果為:" + proceed);
} catch (Throwable e) {
e.printStackTrace();
System.out.println("執(zhí)行方法發(fā)生了錯(cuò)誤:" + e.getMessage());
System.out.println("============== " + className + " 類中的方法:" + methodName + " 執(zhí)行結(jié)束 ==============");
// return setResultError("執(zhí)行方法發(fā)生了錯(cuò)誤:"+e.getMessage());
return "執(zhí)行方法發(fā)生了錯(cuò)誤:" + e.getMessage();
}
System.out.println("============== " + className + " 類中的方法:" + methodName + " 執(zhí)行結(jié)束 ==============");
return proceed;
}
匹配規(guī)則使用技巧:
直接在業(yè)務(wù)方法上加@Around 注解定義切面?
定義切面的三種匹配示例
一:?? ?// 指定類增強(qiáng) DealerController 類名? 該類下所有方法增強(qiáng)
?? ?// @Around(value = "execution(* com.*..DealerController.*(..))")
二:
?? ?// 指定方法增強(qiáng) ?testEncrypt 方法名? ?所有叫testEncrypt 名字的方法增強(qiáng)
?? ?// @Around(value = "execution(* com.*..*.testEncrypt(..))")
三: // DealerController 類名? 該類下所有方法后綴為DealerDecryption的方法 增強(qiáng)
?? ?@Around(value = "execution(* com.*..DealerController.*DealerDecryption(..))")
// 指定類增強(qiáng) DealerController 類名
// @Around(value = "execution(* com.*..DealerController.*(..))")
// 指定方法增強(qiáng) testEncrypt 方法名
// @Around(value = "execution(* com.*..*.testEncrypt(..))")
@Around(value = "execution(* com.*..DealerController.*DealerDecryption(..))")
public Object ReturnDecryption(ProceedingJoinPoint proceedingJoinPoint) {
Object proceed = null;
// String className = proceedingJoinPoint.getSignature().getDeclaringTypeName();
// String methodName = proceedingJoinPoint.getSignature().getName();
// System.out.println("============== " + className + " 類中的方法:" + methodName + " 開始執(zhí)行 ==============");
// MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
// // 參數(shù)名稱
// String[] parameterNames = methodSignature.getParameterNames();
// // 參數(shù)值
// Object[] args = proceedingJoinPoint.getArgs();
// for (int i = 0; i < args.length; i++) {
// System.out.println("第" + i + "個(gè)參數(shù):" + " key:" + parameterNames[i] + " value:" + args[i]);
// }
try {
proceed = proceedingJoinPoint.proceed();
// ====================== 返回==============================
System.out.println("執(zhí)行結(jié)果為:" + proceed);
} catch (Throwable e) {
log.error("=========ReturnDecryption===========",e);
// System.out.println("執(zhí)行方法發(fā)生了錯(cuò)誤:" + e.getMessage());
// System.out.println("============== " + className + " 類中的方法:" + methodName + " 執(zhí)行結(jié)束 ==============");
// return setResultError("執(zhí)行方法發(fā)生了錯(cuò)誤:"+e.getMessage());
return "執(zhí)行方法發(fā)生了錯(cuò)誤:" + e.getMessage();
}
// System.out.println("============== " + className + " 類中的方法:" + methodName + " 執(zhí)行結(jié)束 ==============");
return proceed;
}
到這里切面環(huán)繞的功能基本實(shí)現(xiàn)了, 開始進(jìn)入正文對(duì)業(yè)務(wù)參數(shù)加解密。
讀取返回遇到問(wèn)題:
在通過(guò) proceed = proceedingJoinPoint.proceed(); 方法獲取到了對(duì)象后 雖然可以將參數(shù)打印出來(lái)但是無(wú)法修改返回內(nèi)容和結(jié)果,通過(guò)對(duì)象強(qiáng)轉(zhuǎn)的方式甚至無(wú)法獲取內(nèi)容。
解決方式:
1、對(duì)打印的結(jié)果進(jìn)行 json格式化操作,獲取到具體業(yè)務(wù)返回的數(shù)據(jù)文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-666832.html
2、返回結(jié)果無(wú)法修改,只能造一個(gè)一樣的返回結(jié)果體返回出去文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-666832.html
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.cre.dmp.osp.common.response.RestResponse;
import com.Dealer.service.RemoteCallService;
import com.*.base.core.util.StringUtils;
/**
* Title: WebException.java
* Description: 環(huán)繞增強(qiáng) 對(duì)客戶信息中手機(jī)號(hào) 做解密操作
*
* @date 2023年7月31日
*/
// 開啟切面注解
@Aspect
// lombok日志注解
@Slf4j
// 注入容器,統(tǒng)一管理
@Component
@SuppressWarnings({ "rawtypes", "unchecked" })
public class AopDecrypt {
@Autowired
RemoteCallService remoteCallService;
private static String HANDLE_FIELD_NAME = "mobile"; // 特殊處理字段 TODO
private static final String ENCRYPT_FLAG = "encrypt"; // 加密標(biāo)識(shí)
// (判斷對(duì)值進(jìn)行加密或者解密的操作)
private static final String DECRYPT_FLAG = "decrypt"; // 解密標(biāo)識(shí)(判斷對(duì)值進(jìn)行加密或者解密的操作)
// 指定類增強(qiáng) DealerController 類名
// @Around(value = "execution(* com.*..DealerController.*(..))")
// 指定方法增強(qiáng) testEncrypt 方法名
// @Around(value = "execution(* com.*..*.testEncrypt(..))")
@Around(value = "execution(* com.*..DealerController.*DealerDecryption(..))")
public Object ReturnDecryption(ProceedingJoinPoint proceedingJoinPoint) {
Object proceed = null;
// String className = proceedingJoinPoint.getSignature().getDeclaringTypeName();
// String methodName = proceedingJoinPoint.getSignature().getName();
// System.out.println("============== " + className + " 類中的方法:" + methodName + " 開始執(zhí)行 ==============");
// MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
// // 參數(shù)名稱
// String[] parameterNames = methodSignature.getParameterNames();
// // 參數(shù)值
// Object[] args = proceedingJoinPoint.getArgs();
// for (int i = 0; i < args.length; i++) {
// System.out.println("第" + i + "個(gè)參數(shù):" + " key:" + parameterNames[i] + " value:" + args[i]);
// }
try {
proceed = proceedingJoinPoint.proceed();
ResponseEntity requestParam = (ResponseEntity) proceed;
// 成功 狀態(tài)為200
if (requestParam.getStatusCodeValue() == 200) {
// RestResponse body = (RestResponse)requestParam.getBody();
String ss = JSONObject.toJSONString(requestParam);
log.info("返回的結(jié)果為:"+JSONObject.toJSONString(requestParam));
Object object = JSONObject.parseObject(ss).get("body");
Object object2 = JSONObject.parseObject(object.toString()).get("obj") ;
Object object3 = JSONObject.parseObject(object2.toString()).get("content") ;
if (object3 == null) {
// 單個(gè)對(duì)象加密
Object changValue = changValue(object2,DECRYPT_FLAG);
// 特殊處理數(shù)據(jù)
proceed = RestResponse.createSuccessRes(changValue);
}else{
// 分頁(yè)對(duì)象加密
// list對(duì)象加密
Object changValue = changValue(object3,DECRYPT_FLAG);
// 回填參數(shù)
JSONObject parseObject = JSON.parseObject(object2.toString());
parseObject.put("content", changValue);
proceed =RestResponse.createSuccessRes(parseObject);
}
}
// ====================== 返回==============================
System.out.println("執(zhí)行結(jié)果為:" + proceed);
} catch (Throwable e) {
log.error("=========ReturnDecryption===========",e);
// System.out.println("執(zhí)行方法發(fā)生了錯(cuò)誤:" + e.getMessage());
// System.out.println("============== " + className + " 類中的方法:" + methodName + " 執(zhí)行結(jié)束 ==============");
// return setResultError("執(zhí)行方法發(fā)生了錯(cuò)誤:"+e.getMessage());
return "執(zhí)行方法發(fā)生了錯(cuò)誤:" + e.getMessage();
}
// System.out.println("============== " + className + " 類中的方法:" + methodName + " 執(zhí)行結(jié)束 ==============");
return proceed;
}
/***
*
* @exception 根據(jù)(map\list)類型區(qū)分對(duì)應(yīng)的解析對(duì)象方法:
* @param _obj 內(nèi)容
* @param flag 加密解密 標(biāo)識(shí)字段
* @throws Exception
* void
*/
private Object changValue(Object _obj, String flag) throws Exception {
// 基本類型不作操作
if (_obj instanceof Map) {
changeMapValue(_obj, flag);
} else if (_obj instanceof List) {
List<Object> list = (List<Object>) _obj;
for (Object obj : list) {
if (obj instanceof Map) {
changeMapValue(obj, flag);
} else {
changObjectValue(obj, flag);
}
}
} else {
changObjectValue(_obj, flag);
}
return _obj;
}
/**
*
* @exception 解析map 格式的數(shù)據(jù) 找的需要加密的字段:
* @param _obj
* @param flag 加密解密 標(biāo)識(shí)字段
* @return Object
* @throws Exception
*/
private Object changeMapValue(Object _obj, String flag) throws Exception {
Map<String, Object> map = (Map<String, Object>) _obj;
if (map.containsKey(HANDLE_FIELD_NAME)) {
Object fieldValue = map.get(HANDLE_FIELD_NAME);
String afterValue = crypto(fieldValue, flag);
if (StringUtils.isNotBlank(afterValue)) {
map.put(HANDLE_FIELD_NAME, afterValue);
}
}
return _obj;
}
/***
*
* @exception 解析對(duì)象內(nèi)容 找到需要加密字段:
* @param _obj
* @param flag 加密解密 標(biāo)識(shí)字段
* @return
* @throws Exception Object
*/
private Object changObjectValue(Object _obj, String flag) throws Exception {
Class<?> resultClz = _obj.getClass();
Field[] fieldInfo = resultClz.getDeclaredFields(); // 獲取class里的所有字段
// 父類字段獲取不到
// 注:如果出現(xiàn)加密解密失敗
// 請(qǐng)先查看mobile是否在父類中
for (Field field : fieldInfo) {
if (HANDLE_FIELD_NAME.equals(field.getName())) {
field.setAccessible(true); // 成員變量為private,故必須進(jìn)行此操
Object fieldValue = field.get(_obj);
String afterValue = crypto(fieldValue, flag);
if (StringUtils.isNoneBlank(afterValue)) {
field.set(_obj, afterValue);
}else{
}
break;
}
}
return _obj;
}
/**
* 對(duì)特殊字段 進(jìn)行加解密
* @param value 加密內(nèi)容
* @param flag 加解密方式
* @return
* @throws Exception
*/
private String crypto(Object value, String flag) throws Exception {
if (value == null) {
return null;
}
// 加密操作;加密之前先去查詢一下數(shù)據(jù)庫(kù) 有沒有 如果沒有 則insert
if (ENCRYPT_FLAG.equals(flag)) {
List e1 = new ArrayList();
e1.add(value);
// 加密
Map<String, String> encrypt = remoteCallService.encrypt(e1);
return encrypt.get(value);
} else { // 解密操作 通過(guò)seq 查詢 然后解密返回明文
List e11 = new ArrayList();
e11.add(value);
// 解密
Map<String, String> decrypt = remoteCallService.decrypt(e11);
String mobile = decrypt.get(value);
String result = "";
if (StringUtils.isNoneBlank(mobile)) {
result = mobile.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
}else{
// 解密失敗時(shí) 屏蔽手機(jī)號(hào)
mobile = value.toString();
if (mobile.length()==11) {
result = mobile.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
}else{
result =mobile;
}
}
return result;
}
}
}
到了這里,關(guān)于手機(jī)號(hào)加解密業(yè)務(wù),通過(guò)aop實(shí)現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!