马府强
2 years ago
12 changed files with 477 additions and 0 deletions
@ -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/ |
@ -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> |
@ -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); |
||||
|
} |
||||
|
|
||||
|
} |
@ -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"}; |
||||
|
|
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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); |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
@ -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; |
||||
|
} |
@ -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); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
@ -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 + |
||||
|
'}'; |
||||
|
} |
||||
|
} |
@ -0,0 +1,6 @@ |
|||||
|
server: |
||||
|
port: 8888 |
||||
|
|
||||
|
# 密钥 |
||||
|
aes: |
||||
|
secret: 6hj!xggb@ssg&1ca |
@ -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…
Reference in new issue