From de7e47b821735d37ed2b74e79ebd1745205059dc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=A9=AC=E5=BA=9C=E5=BC=BA?=
<8569561+fuqiangma@user.noreply.gitee.com>
Date: Sat, 1 Apr 2023 21:21:49 +0800
Subject: [PATCH] =?UTF-8?q?=E4=B8=80=E4=B8=AA=E6=B3=A8=E8=A7=A3=E5=AE=9E?=
=?UTF-8?q?=E7=8E=B0=E5=8A=A0=E5=AF=86=E8=A7=A3=E5=AF=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
secret-demo/.gitignore | 33 +++++
secret-demo/pom.xml | 66 +++++++++
.../com/aop/secret/SecretDemoApplication.java | 13 ++
.../com/aop/secret/annotation/Secret.java | 25 ++++
.../java/com/aop/secret/aop/SecretAop.java | 125 ++++++++++++++++++
.../secret/controller/AesTestController.java | 41 ++++++
.../java/com/aop/secret/pojo/UserInfo.java | 14 ++
.../java/com/aop/secret/pojo/UserInfoReq.java | 13 ++
.../java/com/aop/secret/util/AesUtil.java | 52 ++++++++
.../main/java/com/aop/secret/util/Result.java | 76 +++++++++++
.../src/main/resources/application.yml | 6 +
.../secret/SecretDemoApplicationTests.java | 13 ++
12 files changed, 477 insertions(+)
create mode 100644 secret-demo/.gitignore
create mode 100644 secret-demo/pom.xml
create mode 100644 secret-demo/src/main/java/com/aop/secret/SecretDemoApplication.java
create mode 100644 secret-demo/src/main/java/com/aop/secret/annotation/Secret.java
create mode 100644 secret-demo/src/main/java/com/aop/secret/aop/SecretAop.java
create mode 100644 secret-demo/src/main/java/com/aop/secret/controller/AesTestController.java
create mode 100644 secret-demo/src/main/java/com/aop/secret/pojo/UserInfo.java
create mode 100644 secret-demo/src/main/java/com/aop/secret/pojo/UserInfoReq.java
create mode 100644 secret-demo/src/main/java/com/aop/secret/util/AesUtil.java
create mode 100644 secret-demo/src/main/java/com/aop/secret/util/Result.java
create mode 100644 secret-demo/src/main/resources/application.yml
create mode 100644 secret-demo/src/test/java/com/aop/secret/SecretDemoApplicationTests.java
diff --git a/secret-demo/.gitignore b/secret-demo/.gitignore
new file mode 100644
index 0000000..549e00a
--- /dev/null
+++ b/secret-demo/.gitignore
@@ -0,0 +1,33 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
diff --git a/secret-demo/pom.xml b/secret-demo/pom.xml
new file mode 100644
index 0000000..8d7a462
--- /dev/null
+++ b/secret-demo/pom.xml
@@ -0,0 +1,66 @@
+
+
+ * 加解密注解 + *
+ * + * @author 蜗牛 + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Secret { + + // 请求解密的属性,默认手机号和身份证号,可以自行扩展。 + String[] reqPropsName() default {"phone","idCard"}; + + // 响应加密的属性,默认手机号和身份证号,可以自行扩展。 + String[] respPropsName() default {"phone","idCard"}; + +} diff --git a/secret-demo/src/main/java/com/aop/secret/aop/SecretAop.java b/secret-demo/src/main/java/com/aop/secret/aop/SecretAop.java new file mode 100644 index 0000000..19b0ca6 --- /dev/null +++ b/secret-demo/src/main/java/com/aop/secret/aop/SecretAop.java @@ -0,0 +1,125 @@ +package com.aop.secret.aop; + +import com.aop.secret.annotation.Secret; +import com.aop.secret.util.AesUtil; +import com.aop.secret.util.Result; +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.stereotype.Component; +import org.springframework.util.ObjectUtils; +import java.lang.reflect.Method; + +/** + *+ * 加解密注解切面类 + *
+ * + * @author 蜗牛 + */ +@Aspect +@Component +@Slf4j +public class SecretAop { + + /** + * 定义切点,类或方法使用了@Secret注解的地方。 + */ + @Pointcut("@annotation(com.aop.secret.annotation.Secret)") + public void pointcut() { + } + + @Around("pointcut()") + public Object around(ProceedingJoinPoint point) throws Throwable { + // 被代理的对象 + Object target = point.getTarget(); + // 被代理方法的参数 + Object[] args = point.getArgs(); + // 获取通知签名 + MethodSignature signature = (MethodSignature) point.getSignature(); + // 获取被代理的方法 + Method method = target.getClass().getMethod(signature.getName(), signature.getParameterTypes()); + // 获取被代理方法上的注解 + Secret secret = method.getAnnotation(Secret.class); + // 最终返回对象 + Object respObj; + + try { + String[] reqPropsName = secret.reqPropsName(); + String[] respPropsName = secret.respPropsName(); + + // 对请求对象进行解密 + if (!ObjectUtils.isEmpty(reqPropsName)) { + + // 获取请求对象,如果参数只有一个并且是对象,则进行解密处理。 + if (!ObjectUtils.isEmpty(args) && args.length == 1) { + Object arg = args[0]; + // 遍历要解密的属性,和请求对象中的属性进行匹配。 + for (String propName : reqPropsName) { + // 得到匹配上的属性值 + String propVal = (String) arg.getClass().getMethod(getterName(propName)).invoke(arg); + // 解密 + String propDecrypt = AesUtil.decrypt(propVal); + System.out.println("解密--------> " + propName + ": " + propVal + " | " + propDecrypt); + // 设置解密的属性值 + arg.getClass().getMethod(setterName(propName), String.class).invoke(arg, propDecrypt); + } + + } else if (args.length > 1) { + // todo 这里可自行扩展以满足其他请求对象及方式,如多个参数、非对象形式的参数等等。 + } + + } + + // 继续向下执行后得到的响应结果 + respObj = point.proceed(); + + // 对响应对象进行加密 + if (!ObjectUtils.isEmpty(respPropsName)) { + + // 获取响应对象 + Object result = ((Result>)respObj).getData(); + log.info("result: {}", result); + + for (String propName : respPropsName) { + // 获取要加密的属性值 + String propVal = (String) result.getClass().getMethod(getterName(propName)).invoke(result); + // 加密 + String propEncrypt = AesUtil.encrypt(propVal); + System.out.println("加密--------> " + propName + ": " + propVal + " | " + propEncrypt); + // 设置加密的属性值 + result.getClass().getMethod(setterName(propName), String.class).invoke(result, propEncrypt); + } + + } + + } catch (Exception ex) { + // 发生异常记录下日志,但正常执行下去,也可以不try..catch直接通过全局异常处理。 + log.error("加解密异常: {}", ex.getMessage(), ex); + respObj = point.proceed(); + } + + return respObj; + } + + /** + * 转化get方法名 + */ + private String getterName(String name) { + String first = name.substring(0, 1); + String last = name.substring(1); + return "get" + first.toUpperCase() + last; + } + + /** + * 转化set方法名 + */ + private String setterName(String name) { + String first = name.substring(0, 1); + String last = name.substring(1); + return "set" + first.toUpperCase() + last; + } +} diff --git a/secret-demo/src/main/java/com/aop/secret/controller/AesTestController.java b/secret-demo/src/main/java/com/aop/secret/controller/AesTestController.java new file mode 100644 index 0000000..9ee7232 --- /dev/null +++ b/secret-demo/src/main/java/com/aop/secret/controller/AesTestController.java @@ -0,0 +1,41 @@ +package com.aop.secret.controller; + +import com.aop.secret.annotation.Secret; +import com.aop.secret.pojo.UserInfo; +import com.aop.secret.pojo.UserInfoReq; +import com.aop.secret.util.Result; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +/** + * 一个注解搞定SpringBoot接口定制属性加解密 + * @author 蜗牛 + */ +@RestController +@RequestMapping("/api/aes") +@Slf4j +public class AesTestController { + + @GetMapping("/getUser") + @Secret(reqPropsName = {}, respPropsName = {"phone", "idCard", "name"}) + public Result