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