Browse Source

一个注解实现加密解密

pull/2/head
马府强 2 years ago
parent
commit
de7e47b821
  1. 33
      secret-demo/.gitignore
  2. 66
      secret-demo/pom.xml
  3. 13
      secret-demo/src/main/java/com/aop/secret/SecretDemoApplication.java
  4. 25
      secret-demo/src/main/java/com/aop/secret/annotation/Secret.java
  5. 125
      secret-demo/src/main/java/com/aop/secret/aop/SecretAop.java
  6. 41
      secret-demo/src/main/java/com/aop/secret/controller/AesTestController.java
  7. 14
      secret-demo/src/main/java/com/aop/secret/pojo/UserInfo.java
  8. 13
      secret-demo/src/main/java/com/aop/secret/pojo/UserInfoReq.java
  9. 52
      secret-demo/src/main/java/com/aop/secret/util/AesUtil.java
  10. 76
      secret-demo/src/main/java/com/aop/secret/util/Result.java
  11. 6
      secret-demo/src/main/resources/application.yml
  12. 13
      secret-demo/src/test/java/com/aop/secret/SecretDemoApplicationTests.java

33
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/

66
secret-demo/pom.xml

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.aop</groupId>
<artifactId>secret</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>secret-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- Hutool -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.5</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

13
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);
}
}

25
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;
/**
* <p>
* 加解密注解
* </p>
*
* @author 蜗牛
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Secret {
// 请求解密的属性,默认手机号和身份证号,可以自行扩展。
String[] reqPropsName() default {"phone","idCard"};
// 响应加密的属性,默认手机号和身份证号,可以自行扩展。
String[] respPropsName() default {"phone","idCard"};
}

125
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;
/**
* <p>
* 加解密注解切面类
* </p>
*
* @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;
}
}

41
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<UserInfo> 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);
}
}

14
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;
}

13
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;
}

52
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);
}
}

76
secret-demo/src/main/java/com/aop/secret/util/Result.java

@ -0,0 +1,76 @@
package com.aop.secret.util;
public class Result<T> {
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 +
'}';
}
}

6
secret-demo/src/main/resources/application.yml

@ -0,0 +1,6 @@
server:
port: 8888
# 密钥
aes:
secret: 6hj!xggb@ssg&1ca

13
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() {
}
}
Loading…
Cancel
Save