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