1.注解介绍
Annotation
(注解) 是 Java5 开始引入的新特性,可以看作是一种特殊的注释,主要用于修饰类、方法或者变量,提供某些信息供程序在编译或者运行时使用。
注解本质是一个继承了Annotation
的特殊接口:
less
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
public interface Override extends Annotation{
}
注解只有被解析之后才会生效,常见的解析方法有两种:
- 编译期直接扫描:编译器在编译 Java 代码的时候扫描对应的注解并处理,比如某个方法使用
@Override
注解,编译器在编译的时候就会检测当前的方法是否重写了父类对应的方法。 - 运行期通过反射处理:像框架中自带的注解(比如 Spring 框架的
@Value
、@Component
)都是通过反射来进行处理的。
2. 自定义注解实现数据加密
2.1 创建自定义注解,自定义注解的创建需要使用 @interface
关键字
less
@Target({ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EncryptMethod {
String[] fields() default ""; // 用于指定需要加密的字段名称
}
@Target
:定义注解可以应用的目标元素类型。@Retention
:定义注解的保留策略(源码、编译时、运行时)。@Documented
:标记注解类型,使其在 JavaDoc 文档中可见。
2.2 解析自定义注解
typescript
@Aspect
@Component
public class EncryptAspect {
@PostConstruct
public void init() {
System.out.println("EncryptAspect initialized.");
}
/* ---------- 切点 ---------- */
@Pointcut("@annotation(encryptMethod)")
public void encryptPointcut(EncryptMethod encryptMethod) {}
/* ---------- 环绕通知 ---------- */
@Around("encryptPointcut(encryptMethod)")
public Object encryptFields(ProceedingJoinPoint jp, EncryptMethod encryptMethod) throws Throwable {
System.out.println("EncryptAspect invoked for: " + jp.getSignature());
// 1. 处理入参
Object[] args = jp.getArgs();
if (args != null) {
for (Object arg : args) {
if (arg == null) continue;
for (String fieldName : encryptMethod.fields()) {
encryptField(arg, fieldName);
}
}
}
// 2. 执行原方法
Object result = jp.proceed();
// 3. 处理返回值
if (result != null) {
// 情况1:直接返回 List
if (result instanceof Collection<?>) {
for (Object item : (Collection<?>) result) {
for (String field : encryptMethod.fields()) {
encryptField(item, field);
}
}
}
// 情况2:返回 TableDataInfo
else if (result instanceof TableDataInfo) {
TableDataInfo table = (TableDataInfo) result;
List<?> rows = table.getRows();
if (rows != null) {
for (Object row : rows) {
for (String field : encryptMethod.fields()) {
encryptField(row, field);
}
}
}
}
// 情况3:单个对象
else {
for (String field : encryptMethod.fields()) {
encryptField(result, field);
}
}
}
return result;
}
/* ---------- 反射加密指定字段 ---------- */
private void encryptField(Object target, String fieldName) {
try {
Field field = findField(target.getClass(), fieldName);
if (field == null) return;
field.setAccessible(true);
Object value = field.get(target);
if (value instanceof String) {
String cipher = encrypt((String) value);
field.set(target, cipher);
}
} catch (Exception e) {
// 记录日志即可,不要阻断业务
e.printStackTrace();
}
}
/* ---------- 递归找字段(支持父类) ---------- */
private Field findField(Class<?> clazz, String fieldName) {
for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
try {
return clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException ignored) {}
}
return null;
}
/* ---------- AES 加密 ---------- */
private String encrypt(String plain) {
try {
String key = "XnUFyFy+9G0JWc9LwaYAWw==";
byte[] cipherBytes = AES.encrypt(plain.getBytes(StandardCharsets.UTF_8),
key.getBytes(), AES.ALGORITHM_AEPP);
return Base64.getUrlEncoder().withoutPadding().encodeToString(cipherBytes);
} catch (Exception e) {
e.printStackTrace();
return plain;
}
}
}
切面:EncryptAspect
@Aspect
:标记该类为一个切面类。
切点:
java
@Pointcut("@annotation(encryptMethod)")
@Pointcut
:定义一个切点,指定哪些方法会被拦截。@annotation(encryptMethod)
:匹配所有被@EncryptMethod
注解标记的方法。
连接点:
java
public Object encryptFields(ProceedingJoinPoint jp, EncryptMethod encryptMethod) throws Throwable {
ProceedingJoinPoint
:提供对目标方法的访问,可以通过 proceed()
方法执行目标方法
通知:
java
@Around("encryptPointcut(encryptMethod)")
@Around
:定义一个环绕通知,用于在方法执行前后执行自定义逻辑。
总结执行逻辑:
- 定义切点 :匹配被
@EncryptMethod
注解标记的方法。 - 环绕通知:在方法执行前后执行自定义逻辑。
- 处理入参:对方法的参数中的指定字段进行加密。
- 执行原方法:调用目标方法。
- 处理返回值:对返回值中的指定字段进行加密。
- 反射加密:通过反射机制访问和修改字段值。
- AES 加密:使用 AES 算法对字段值进行加密。
这种实现方式可以灵活地对方法的参数和返回值进行加密处理,适用于需要对敏感数据进行加密的场景。