package com.monkeylessey.framework.aspect;
|
|
import com.monkeylessey.annotation.Cipher;
|
import com.monkeylessey.annotation.CipherField;
|
import com.monkeylessey.framework.service.cipher.CipherService;
|
import com.monkeylessey.response.Result;
|
import org.aspectj.lang.ProceedingJoinPoint;
|
import org.aspectj.lang.annotation.AfterReturning;
|
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.Qualifier;
|
import org.springframework.stereotype.Component;
|
import org.springframework.util.CollectionUtils;
|
|
import java.lang.annotation.Annotation;
|
import java.lang.reflect.Field;
|
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.Method;
|
import java.util.Arrays;
|
import java.util.Objects;
|
|
@Aspect
|
@Component
|
public class AESAspectHandler {
|
|
private final CipherService cipherService;
|
|
public AESAspectHandler(@Qualifier("AESCipherService") CipherService cipherService) {
|
this.cipherService = cipherService;
|
}
|
|
|
@Pointcut(value = "execution(* com.monkeylessey.controller.*.*(..,@com.monkeylessey.annotation.Cipher (*),..))")
|
public void point() {}
|
|
@Pointcut(value = "@annotation(com.monkeylessey.annotation.Cipher)")
|
public void returnPoint() {}
|
|
@AfterReturning(value = "returnPoint()", returning = "result")
|
public void afterReturn(Object result) throws Exception {
|
if (result.getClass() == Result.class) {
|
Result res = (Result) result;
|
Object data = res.get(Result.DATA);
|
Class<?> dataClass = data.getClass();
|
Field[] fields = dataClass.getDeclaredFields();
|
for (Field field : fields) {
|
this.iterationCipherTargetField(false, data, dataClass, field);
|
}
|
}
|
}
|
|
/**
|
* 必须使用@Around才能处理直接用String作为参数的接口
|
* @throws Throwable
|
*/
|
@Around(value = "point()")
|
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
|
Object[] args = joinPoint.getArgs();
|
|
if (CollectionUtils.isEmpty(Arrays.asList(args))) {
|
return;
|
}
|
|
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
Method method = signature.getMethod();
|
// 二维数组:第几个参数;参数上的注解
|
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
|
for (int i = 0; i < parameterAnnotations.length; i++) {
|
for (Annotation annotation : parameterAnnotations[i]) {
|
if (annotation instanceof Cipher) {
|
Object arg = args[i];
|
Class<?> argClass = arg.getClass();
|
// 检查注解不能作用在Object类型上
|
this.checkAnnotationOnObject(argClass);
|
// 如果注解作用于String,直接解密
|
if (argClass == String.class) {
|
String decrypt = cipherService.decode((String) arg);
|
args[i] = decrypt;
|
continue;
|
}
|
// 获取目标对象的所有属性
|
Field[] fields = argClass.getDeclaredFields();
|
// 遍历,找到添加了CipherField注解的String类型属性
|
for (Field field : fields) {
|
this.iterationCipherTargetField(true, arg, argClass, field);
|
}
|
}
|
}
|
}
|
// 执行目标接口
|
joinPoint.proceed(args);
|
}
|
|
/**
|
* 迭代对象 解密、加密 String字符串
|
*
|
* @param arg 接口参数
|
* @param argClass 接口参数类型
|
* @param field 接口参数对象的属性
|
* @throws Exception
|
*/
|
public void iterationCipherTargetField(boolean decode, Object arg, Class argClass, Field field) throws Exception {
|
if (Objects.isNull(arg)) {
|
return;
|
}
|
CipherField annotation = field.getAnnotation(CipherField.class);
|
if (Objects.isNull(annotation)) {
|
return;
|
}
|
Class<?> fieldClass = field.getType();
|
this.checkAnnotationOnObject(fieldClass);
|
// 未开启解码,则无操作
|
if (decode && ! annotation.needDecode()) {
|
return;
|
}
|
// 未开启编码,则无操作
|
if (! decode && ! annotation.needEncode()) {
|
return;
|
}
|
|
if (fieldClass == String.class) { // String类型直接解密
|
this.cipherByReflection(decode, arg, argClass, field);
|
} else { // 对象类型,需要判断对象的属性是否有CipherField注解的字符串,如果属性还是对象则继续深入遍历
|
Field[] childObjectFields = fieldClass.getDeclaredFields();
|
for (Field childObjectField : childObjectFields) {
|
this.iterationCipherTargetField(decode, getValueByReflection(arg, field), fieldClass, childObjectField);
|
}
|
}
|
}
|
|
|
/**
|
* 通过反射修改密文为明文
|
* @param decode 是否解密
|
* @param arg
|
* @param argClass
|
* @param field
|
* @throws Exception
|
*/
|
public void cipherByReflection(boolean decode, Object arg, Class argClass, Field field) throws Exception {
|
String fieldName = field.getName();
|
Method[] methods = argClass.getDeclaredMethods();
|
Method get = null; // 声明get方法
|
Method set = null; // 声明set方法
|
// 遍历目标对象的所有方法,主要是拿到目标属性的get、set方法
|
for (Method method : methods) {
|
if (method.getName().equals(this.getMethodByFiledName("set", fieldName))) {
|
set = method;
|
}
|
if (method.getName().equals(this.getMethodByFiledName("get", fieldName))) {
|
get = method;
|
}
|
}
|
// 必须要定义set、get方法,否则无法通过反射将密文修改为明文
|
if (Objects.isNull(set)) {
|
throw new RuntimeException("请为需要解密的字段添加【set】方法");
|
}
|
if (Objects.isNull(get)) {
|
throw new RuntimeException("请为需要解密的字段添加【get】方法");
|
}
|
if (decode) {
|
// 解密
|
set.invoke(arg, cipherService.decode((String) get.invoke(arg)));
|
} else {
|
// 加密
|
set.invoke(arg, cipherService.encode((String) get.invoke(arg)));
|
}
|
}
|
|
/**
|
* 主要是获取,属性为对象的值,用于深度遍历
|
* @param arg 持有这个属性的对象
|
* @param field
|
* @return
|
* @throws InvocationTargetException
|
* @throws IllegalAccessException
|
*/
|
public Object getValueByReflection(Object arg, Field field) throws InvocationTargetException, IllegalAccessException {
|
Method[] methods = arg.getClass().getDeclaredMethods();
|
for (Method method : methods) {
|
if (method.getName().equals(this.getMethodByFiledName("get", field.getName()))) {
|
return method.invoke(arg);
|
}
|
}
|
return null;
|
}
|
|
/**
|
* 检查注解不能作用在Object类型上
|
* @param target
|
*/
|
public void checkAnnotationOnObject(Class target) {
|
if (target == Object.class) {
|
throw new RuntimeException("CipherField、CipherField注解不能作用在Object类型上");
|
}
|
}
|
|
/**
|
* 获取方法名,属性名首字母大写
|
*
|
* @param prefix
|
* @param fieldName
|
* @return
|
*/
|
public String getMethodByFiledName(String prefix, String fieldName) {
|
String firstWord = fieldName.substring(0,1).toUpperCase();
|
String method = prefix + firstWord + fieldName.substring(1);
|
return method;
|
}
|
|
|
}
|