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 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.7.2
+
+
+ com.aop
+ secret
+ 0.0.1-SNAPSHOT
+ secret-demo
+ Demo project for Spring Boot
+
+ 1.8
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
+
+ cn.hutool
+ hutool-all
+ 5.8.5
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+
+
+
diff --git a/secret-demo/src/main/java/com/aop/secret/SecretDemoApplication.java b/secret-demo/src/main/java/com/aop/secret/SecretDemoApplication.java
new file mode 100644
index 0000000..8ccb757
--- /dev/null
+++ b/secret-demo/src/main/java/com/aop/secret/SecretDemoApplication.java
@@ -0,0 +1,13 @@
+package com.aop.secret;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SecretDemoApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SecretDemoApplication.class, args);
+ }
+
+}
diff --git a/secret-demo/src/main/java/com/aop/secret/annotation/Secret.java b/secret-demo/src/main/java/com/aop/secret/annotation/Secret.java
new file mode 100644
index 0000000..c3703ed
--- /dev/null
+++ b/secret-demo/src/main/java/com/aop/secret/annotation/Secret.java
@@ -0,0 +1,25 @@
+package com.aop.secret.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ *
+ * 加解密注解
+ *
+ *
+ * @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 getUser() {
+
+ // 模拟返回用户信息
+ UserInfo userInfo = new UserInfo();
+ userInfo.setId(1001L);
+ userInfo.setName("蜗牛");
+ userInfo.setAge(500);
+ userInfo.setIdCard("320125199801253349");
+ userInfo.setPhone("15912345678");
+ userInfo.setAddress("蜗牛洞");
+
+ return new Result(HttpStatus.OK.value(), "查询成功", userInfo);
+ }
+
+ @PostMapping("/saveUser")
+ @Secret(reqPropsName = {"phone", "idCard", "name"}, respPropsName = {})
+ public Result saveUser(@RequestBody UserInfoReq userInfoReq) {
+ return new Result(HttpStatus.OK.value(), "保存成功", userInfoReq);
+ }
+}
diff --git a/secret-demo/src/main/java/com/aop/secret/pojo/UserInfo.java b/secret-demo/src/main/java/com/aop/secret/pojo/UserInfo.java
new file mode 100644
index 0000000..ca50668
--- /dev/null
+++ b/secret-demo/src/main/java/com/aop/secret/pojo/UserInfo.java
@@ -0,0 +1,14 @@
+package com.aop.secret.pojo;
+
+import lombok.Data;
+
+
+@Data
+public class UserInfo {
+ private Long id;
+ private String name;
+ private Integer age;
+ private String idCard;
+ private String phone;
+ private String address;
+}
diff --git a/secret-demo/src/main/java/com/aop/secret/pojo/UserInfoReq.java b/secret-demo/src/main/java/com/aop/secret/pojo/UserInfoReq.java
new file mode 100644
index 0000000..57f6ae6
--- /dev/null
+++ b/secret-demo/src/main/java/com/aop/secret/pojo/UserInfoReq.java
@@ -0,0 +1,13 @@
+package com.aop.secret.pojo;
+
+import lombok.Data;
+
+
+@Data
+public class UserInfoReq {
+ private String name;
+ private Integer age;
+ private String idCard;
+ private String phone;
+ private String address;
+}
diff --git a/secret-demo/src/main/java/com/aop/secret/util/AesUtil.java b/secret-demo/src/main/java/com/aop/secret/util/AesUtil.java
new file mode 100644
index 0000000..71669d4
--- /dev/null
+++ b/secret-demo/src/main/java/com/aop/secret/util/AesUtil.java
@@ -0,0 +1,52 @@
+package com.aop.secret.util;
+
+import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
+import cn.hutool.crypto.symmetric.SymmetricCrypto;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import java.nio.charset.StandardCharsets;
+
+
+@Component
+public class AesUtil {
+
+ // AES工具声明
+ private static SymmetricCrypto aes;
+
+ // 密钥字符串
+ private static String secret;
+
+ @Value("${aes.secret}")
+ public void setSecret(String secret) {
+ AesUtil.secret = secret;
+ }
+
+ @PostConstruct
+ public void init() {
+ // AES工具构建
+ byte[] key = secret.getBytes(StandardCharsets.UTF_8);
+ aes = new SymmetricCrypto(SymmetricAlgorithm.AES, key);
+ }
+
+ /**
+ * 加密
+ * @param content 内容
+ * @return 加密结果
+ */
+ public static String encrypt(String content) {
+ return aes.encryptHex(content);
+ }
+
+ /**
+ * 解密
+ * @param encryptStr 加密内容
+ * @return 解密结果
+ */
+ public static String decrypt(String encryptStr) {
+ return aes.decryptStr(encryptStr);
+ }
+
+
+}
diff --git a/secret-demo/src/main/java/com/aop/secret/util/Result.java b/secret-demo/src/main/java/com/aop/secret/util/Result.java
new file mode 100644
index 0000000..ef99774
--- /dev/null
+++ b/secret-demo/src/main/java/com/aop/secret/util/Result.java
@@ -0,0 +1,76 @@
+package com.aop.secret.util;
+
+public class Result {
+
+ private int code;
+ private String msg;
+ private T data;
+
+ public Result(int code, String msg, T data) {
+ this.code = code;
+ this.msg = msg;
+ this.data = data;
+ }
+ public Result(int code, String msg) {
+ this.code = code;
+ this.msg = msg;
+ }
+
+ public Result() {
+
+ }
+
+ /**
+ * 构建消息内容
+ * @param msg
+ * @return
+ */
+ public Result buildMessage(String msg){
+ this.setMsg(msg);
+ return this;
+ }
+
+ /**
+ * 构建消息data的值,key默认为data
+ * @param obj data值
+ * @return
+ */
+ public Result buildData(T obj){
+ this.setData(obj);
+ return this;
+
+ }
+
+ public int getCode() {
+ return code;
+ }
+
+ public void setCode(int code) {
+ this.code = code;
+ }
+
+ public String getMsg() {
+ return msg;
+ }
+
+ public void setMsg(String msg) {
+ this.msg = msg;
+ }
+
+ public T getData() {
+ return data;
+ }
+
+ public void setData(T data) {
+ this.data = data;
+ }
+
+ @Override
+ public String toString() {
+ return "Result{" +
+ "code=" + code +
+ ", msg='" + msg + '\'' +
+ ", data=" + data +
+ '}';
+ }
+}
diff --git a/secret-demo/src/main/resources/application.yml b/secret-demo/src/main/resources/application.yml
new file mode 100644
index 0000000..915f6a4
--- /dev/null
+++ b/secret-demo/src/main/resources/application.yml
@@ -0,0 +1,6 @@
+server:
+ port: 8888
+
+# 密钥
+aes:
+ secret: 6hj!xggb@ssg&1ca
\ No newline at end of file
diff --git a/secret-demo/src/test/java/com/aop/secret/SecretDemoApplicationTests.java b/secret-demo/src/test/java/com/aop/secret/SecretDemoApplicationTests.java
new file mode 100644
index 0000000..b1d4272
--- /dev/null
+++ b/secret-demo/src/test/java/com/aop/secret/SecretDemoApplicationTests.java
@@ -0,0 +1,13 @@
+package com.aop.secret;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class SecretDemoApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+
+}