马府强
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