序言:
在开发过程中因为功能的特殊性 需要对客户信息的手机号进行加密处理 然后存到数据库中,然而这个需求是项目中期加入,很多功能上已经使用了获取客户信息的方法,没法统一控制手机号加解密操作, 于是考虑使用 aop做环绕增强 对所有出参进行,解密操作(这里只对)。
Spring AOP--@Around--使用/实例 使用或理解上的问题
tips: 这里不赘述 注解的含义等基本概念,是以使用@Around为主的新手教程
需要引入jar包
<!-- #Spring中的切面依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
对于aop 网上有很多的代码示例,但是有些看不明白、写得比较含糊的问题列一下
问题1 @Around中的方法名称怎么定义:
@Around("test()") 这个中的 test()方法是自己定义的切面的方法 必须要通过@Pointcut指定切面才能生效
// 指定全局的切面
@Pointcut("execution (* com.dealer.api.controller.*.*(..))")
public void test() {
}
// 环绕增强
@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 + " 开始执行 ==============");
MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
// 参数名称
String[] parameterNames = methodSignature.getParameterNames();
// 参数值
Object[] args = proceedingJoinPoint.getArgs();
for (int i = 0; i < args.length; i++) {
System.out.println("第" + i + "个参数:" + " key:" + parameterNames[i] + " value:" + args[i]);
}
try {
proceed = proceedingJoinPoint.proceed();
// ====================== 返回==============================
System.out.println("执行结果为:" + proceed);
} catch (Throwable e) {
e.printStackTrace();
System.out.println("执行方法发生了错误:" + e.getMessage());
System.out.println("============== " + className + " 类中的方法:" + methodName + " 执行结束 ==============");
// return setResultError("执行方法发生了错误:"+e.getMessage());
return "执行方法发生了错误:" + e.getMessage();
}
System.out.println("============== " + className + " 类中的方法:" + methodName + " 执行结束 ==============");
return proceed;
}
匹配规则使用技巧:
直接在业务方法上加@Around 注解定义切面
定义切面的三种匹配示例
一: // 指定类增强 DealerController 类名 该类下所有方法增强
// @Around(value = "execution(* com.*..DealerController.*(..))")
二:
// 指定方法增强 testEncrypt 方法名 所有叫testEncrypt 名字的方法增强
// @Around(value = "execution(* com.*..*.testEncrypt(..))")
三: // DealerController 类名 该类下所有方法后缀为DealerDecryption的方法 增强
@Around(value = "execution(* com.*..DealerController.*DealerDecryption(..))")
// 指定类增强 DealerController 类名
// @Around(value = "execution(* com.*..DealerController.*(..))")
// 指定方法增强 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 + " 开始执行 ==============");
// MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
// // 参数名称
// String[] parameterNames = methodSignature.getParameterNames();
// // 参数值
// Object[] args = proceedingJoinPoint.getArgs();
// for (int i = 0; i < args.length; i++) {
// System.out.println("第" + i + "个参数:" + " key:" + parameterNames[i] + " value:" + args[i]);
// }
try {
proceed = proceedingJoinPoint.proceed();
// ====================== 返回==============================
System.out.println("执行结果为:" + proceed);
} catch (Throwable e) {
log.error("=========ReturnDecryption===========",e);
// System.out.println("执行方法发生了错误:" + e.getMessage());
// System.out.println("============== " + className + " 类中的方法:" + methodName + " 执行结束 ==============");
// return setResultError("执行方法发生了错误:"+e.getMessage());
return "执行方法发生了错误:" + e.getMessage();
}
// System.out.println("============== " + className + " 类中的方法:" + methodName + " 执行结束 ==============");
return proceed;
}
到这里切面环绕的功能基本实现了, 开始进入正文对业务参数加解密。
读取返回遇到问题:
在通过 proceed = proceedingJoinPoint.proceed(); 方法获取到了对象后 虽然可以将参数打印出来但是无法修改返回内容和结果,通过对象强转的方式甚至无法获取内容。
解决方式:
1、对打印的结果进行 json格式化操作,获取到具体业务返回的数据文章来源:https://www.toymoban.com/news/detail-666832.html
2、返回结果无法修改,只能造一个一样的返回结果体返回出去文章来源地址https://www.toymoban.com/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: 环绕增强 对客户信息中手机号 做解密操作
*
* @date 2023年7月31日
*/
// 开启切面注解
@Aspect
// lombok日志注解
@Slf4j
// 注入容器,统一管理
@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"; // 加密标识
// (判断对值进行加密或者解密的操作)
private static final String DECRYPT_FLAG = "decrypt"; // 解密标识(判断对值进行加密或者解密的操作)
// 指定类增强 DealerController 类名
// @Around(value = "execution(* com.*..DealerController.*(..))")
// 指定方法增强 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 + " 开始执行 ==============");
// MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
// // 参数名称
// String[] parameterNames = methodSignature.getParameterNames();
// // 参数值
// Object[] args = proceedingJoinPoint.getArgs();
// for (int i = 0; i < args.length; i++) {
// System.out.println("第" + i + "个参数:" + " key:" + parameterNames[i] + " value:" + args[i]);
// }
try {
proceed = proceedingJoinPoint.proceed();
ResponseEntity requestParam = (ResponseEntity) proceed;
// 成功 状态为200
if (requestParam.getStatusCodeValue() == 200) {
// RestResponse body = (RestResponse)requestParam.getBody();
String ss = JSONObject.toJSONString(requestParam);
log.info("返回的结果为:"+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) {
// 单个对象加密
Object changValue = changValue(object2,DECRYPT_FLAG);
// 特殊处理数据
proceed = RestResponse.createSuccessRes(changValue);
}else{
// 分页对象加密
// list对象加密
Object changValue = changValue(object3,DECRYPT_FLAG);
// 回填参数
JSONObject parseObject = JSON.parseObject(object2.toString());
parseObject.put("content", changValue);
proceed =RestResponse.createSuccessRes(parseObject);
}
}
// ====================== 返回==============================
System.out.println("执行结果为:" + proceed);
} catch (Throwable e) {
log.error("=========ReturnDecryption===========",e);
// System.out.println("执行方法发生了错误:" + e.getMessage());
// System.out.println("============== " + className + " 类中的方法:" + methodName + " 执行结束 ==============");
// return setResultError("执行方法发生了错误:"+e.getMessage());
return "执行方法发生了错误:" + e.getMessage();
}
// System.out.println("============== " + className + " 类中的方法:" + methodName + " 执行结束 ==============");
return proceed;
}
/***
*
* @exception 根据(map\list)类型区分对应的解析对象方法:
* @param _obj 内容
* @param flag 加密解密 标识字段
* @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 格式的数据 找的需要加密的字段:
* @param _obj
* @param flag 加密解密 标识字段
* @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 解析对象内容 找到需要加密字段:
* @param _obj
* @param flag 加密解密 标识字段
* @return
* @throws Exception Object
*/
private Object changObjectValue(Object _obj, String flag) throws Exception {
Class<?> resultClz = _obj.getClass();
Field[] fieldInfo = resultClz.getDeclaredFields(); // 获取class里的所有字段
// 父类字段获取不到
// 注:如果出现加密解密失败
// 请先查看mobile是否在父类中
for (Field field : fieldInfo) {
if (HANDLE_FIELD_NAME.equals(field.getName())) {
field.setAccessible(true); // 成员变量为private,故必须进行此操
Object fieldValue = field.get(_obj);
String afterValue = crypto(fieldValue, flag);
if (StringUtils.isNoneBlank(afterValue)) {
field.set(_obj, afterValue);
}else{
}
break;
}
}
return _obj;
}
/**
* 对特殊字段 进行加解密
* @param value 加密内容
* @param flag 加解密方式
* @return
* @throws Exception
*/
private String crypto(Object value, String flag) throws Exception {
if (value == null) {
return null;
}
// 加密操作;加密之前先去查询一下数据库 有没有 如果没有 则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 { // 解密操作 通过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{
// 解密失败时 屏蔽手机号
mobile = value.toString();
if (mobile.length()==11) {
result = mobile.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
}else{
result =mobile;
}
}
return result;
}
}
}
到了这里,关于手机号加解密业务,通过aop实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!