(10条消息)springboot⾃定义注解使⽤AOP实现请求参数解密以及响应数据加密
⼀、前⾔
本篇⽂章将依托与SpringBoot平台,⾃定义注解⽤来标识接⼝请求是否实现加密解密。使⽤AOP切⾯来具体操作解密加密,实现对源代码的低耦合,不在原基础上做很⼤的改动。
本篇⽂章的所有⽰例,都上传到我的github中,欢迎⼤家拉取测试,欢迎star github
实现要求:
1. ⾃定义⼀个注解@Secret,⽤来标识需要实现加密解密
作⽤在Controller类上,表⽰此Controller类的所有接⼝都实现加密解密
作⽤来单⼀⽅法上,表⽰此接⼝⽅法需要实现加密解密
2. 使⽤AOP切⾯编程实现
在接⼝⽅法执⾏之前将前端的加密参数解密并重新赋给接⼝参数
在接⼝⽅法响应之后,将返回的数据进⾏加密返回
3. 在配置⽂件中配置,是否开启全局的加密解密操作
实现流程:
1. 前端请求的接⼝将请求参数json通过AES加密⽣成加密字符串,然后将加密字符串通过名为encyptStr字段传递给后端。
2. AOP前置⽅法拦截,将encyptStr字符串通过AES解密得到原始请求参数json,将json映射为请求⽅法的参数对象User。
3. 接⼝通过参数成功响应,并将响应数据直接返回。
4. AOP后置⽅式拦截,将响应参数data字段⾥的数据AES加密,并返回给前端
5. 前端收到请求响应,通过code判断请求是否成功,AES加密data字段得到需要的数据。
⼆、实现操作
1. 创建SPRINGBOOT项⽬
创建⼀个SpringBoot项⽬,导⼊必要的maven依赖。
使⽤AOP切⾯需要导⼊AOP的启动器
lombok是⼀个通过注解简化代码的⼯具,在idea中使⽤需要安装lombok插件
json转换⼯具,apache⼯具类
1. <!-- web依赖 -->
2. <dependency>
3. <groupId>org.springframework.boot</groupId>
4. <artifactId>spring-boot-starter-web</artifactId>
5. </dependency>
6.
7. <!-- AOP切⾯依赖 -->
8. <dependency>
9. <groupId>org.springframework.boot</groupId>
10. <artifactId>spring-boot-starter-aop</artifactId>
11. </dependency>
12.
13. <!-- lombok⼯具 -->
14. <dependency>
15. <groupId>org.projectlombok</groupId>
16. <artifactId>lombok</artifactId>
17. <optional>true</optional>
18. </dependency>
19.
20. <!-- json操作类 -->
21. <dependency>
22. <groupId>com.alibaba</groupId>
23. <artifactId>fastjson</artifactId>
24. <version>1.2.52.sec06</version>
25. </dependency>spring aop应用场景
26. <!-- String⼯具包 -->
27. <dependency>
28. <groupId>org.apachemons</groupId>
29. <artifactId>commons-lang3</artifactId>
30. <version>3.9</version>
31. </dependency>
2. ⾃定注解@SECRET
我们通过⾃定义的注解,来标识类或接⼝,告诉AOP哪些类或⽅法需要执⾏加密解密操作,更加的灵活。
Secret.java
1. package com.agger.springbootaopdemo.annotation;
2.
3. import java.lang.annotation.ElementType;
4. import java.lang.annotation.Retention;
5. import java.lang.annotation.RetentionPolicy;
6. import java.lang.annotation.Target;
7.
8. /**
9. * @classname: Secret
10. * @description: ⾃定义注解,⽤来标识请求类或者⽅法是否使⽤AOP加密解密
11. * @author chenhx
12. * @date 2019-12-05 13:48:03
13. */
14. @Target({ElementType.TYPE,ElementType.METHOD}) // 可以作⽤在类上和⽅法上
15. @Retention(RetentionPolicy.RUNTIME) // 运⾏时起作⽤
16. public @interface Secret {
17.
18. // 参数类(⽤来传递加密数据,只有⽅法参数中有此类或此类的⼦类才会执⾏加解密)
19. Class value();
20.
21. // 参数类中传递加密数据的属性名,默认encryptStr
22. String encryptStrName() default "encryptStr";
23. }
⾃定义注解很简单,只需要确定注解的作⽤位置和运⾏时机。其中有两个变量value和encryptStrName。
value没有默认值,是必传的参数,⽤来表⽰需要加解密的参数类或⽗类。AOP中或⽤到。
encryptStrName默认值为"encryptStr",⽤来表⽰前端传递的加密参数名称是什么,value类中必须存在此字段
3. CONTROLLER中使⽤
定义好@Secret注解后,我们就可以在Controller中使⽤了,不过现在只相当于是⼀个标注,还没有起任何作⽤,需要我们再定义好AOP后才会起作⽤。
1. @Secret注解作⽤来类上
UserController.java
1. package com.ller;
2.
3. import com.agger.springbootaopdemo.annotation.Secret;
4. import com.agger.springbootaopdemo.vo.BaseVO;
5. import com.agger.springbootaopdemo.vo.ResultVO;
6. import com.agger.springbootaopdemo.vo.UserVO;
7. import org.springframework.web.bind.annotation.*;
8.
9. import java.util.HashMap;
10. import java.util.Iterator;
11. import java.util.Map;
12.
13. /**
14. * @program: UserController
15. * @description: ⽤户控制类
16. * @author: chenhx
17. * @create: 2019-12-03 15:22
18. **/
19. @Secret(BaseVO.class) //接⼝参数和返回要进⾏加解密
20. @RestController
21. @RequestMapping("user")
22. public class UserController {
23.
24. //采⽤内部类的实例代码块⽅式初始化map
25. HashMap<Integer, UserVO> userMap = new HashMap<Integer, UserVO>(){
26. {
27. put(1,new UserVO(1,"张三"));
28. put(2,new UserVO(2,"李四"));
29. put(3,new UserVO(3,"王五"));
30. }
31. };
32.
33. // 通过id查询⽤户
34. @GetMapping("getUserName/{id}")
35. public ResultVO getUserName(@PathVariable("id") Integer id){
36. return new ResultVO(0,"查询成功",(id));
37. }
38.
39. // 通过name查询⽤户id
40. @GetMapping("getUserId")
41. public ResultVO getUserId(@RequestParam String name){
42. Iterator<Map.Entry<Integer, UserVO>> iterator = Set().iterator();
43. UserVO u = null;
44. while (iterator.hasNext()){
45. Map.Entry<Integer, UserVO> entry = ();
46. Value().getName().equals(name)){
47. u = Value();
48. break;
49. }
50. }
51. return new ResultVO(0,"查询成功",u);
52. }
53.
54. // 新增⽤户
55. @PostMapping("addUser")
56. public ResultVO addUser(@RequestBody UserVO user){
57. return new ResultVO(0,"新增成功",user);
58. }
59.
60. // 更改⽤户
61. @PostMapping("updateUser")
62. public ResultVO updateUser(@RequestBody UserVO user) throws Throwable {
63. if(user==null||Id()==null){
64. throw new NullPointerException();
65. }else{
66. return new ResultVO(0,"修改成功",user);
67. }
68. }
69. }
@Secret(BaseVO.class)定义在了UserController类上,表⽰整个类下⾯的⽅法都会实现AOP加密解密。BaseVO是所有vo的基类,其中只定义了⼀个字段encryptStr,也就是前端传递的加密参数字段。
BaseVO.java
1. package com.agger.springbootaopdemo.vo;
2.
3. import lombok.Data;
4.
5. /**
6. * @program: BaseVO
7. * @description: 基类
8. * @author: chenhx
9. * @create: 2019-12-05 15:15
10. **/
11. @Data
12. public class BaseVO {
13. // 加密密⽂
14. private String encryptStr;
15. }
UserVO.java
1. package com.agger.springbootaopdemo.vo;
2.
3. import lombok.AllArgsConstructor;
4. import lombok.Data;
5. import lombok.NoArgsConstructor;
6.
7. /**
8. * @program: User
9. * @description: ⽤户
10. * @author: chenhx
11. * @create: 2019-12-03 15:31
12. **/
13. @Data
14. @NoArgsConstructor
15. @AllArgsConstructor
16. public class UserVO extends BaseVO{
17. private Integer id;
18. private String name;
19. }
1. @Secret注解作⽤在⽅法上,表⽰只有此⽅法才需要执⾏AOP加密解密
DeptController.java
1. package com.ller;
2.
3. import com.agger.springbootaopdemo.annotation.Secret;
4. import com.agger.springbootaopdemo.vo.DeptVO;
5. import com.agger.springbootaopdemo.vo.ResultVO;
6. import org.springframework.web.bind.annotation.*;
7.
8. /**
9. * @program: Dept
10. * @description: 部门类
11. * @author: chenhx
12. * @create: 2019-12-03 15:26
13. **/
14. @RestController
15. @RequestMapping("dept")
16. public class DeptController {
17.
18. @GetMapping("getDeptName/{id}")
19. public ResultVO getDeptName(@PathVariable("id") String id){
20. return new ResultVO(0,"查询成功","财务部" + id);
21. }
22.
23. // 注解在⽅法上,并传递了encryptStrName⾃⼰定义的加密字符串名称encryptJson
24. @Secret(value = DeptVO.class,encryptStrName = "encryptJson")
25. @PostMapping("addDept")
26. public ResultVO addDept(@RequestBody DeptVO dept){
27. return new ResultVO(0,"新增成功",dept);
28. }
29.
30. }
DeptVO类没有继承BaseVO类,⾃⼰写了⼀个前端需要传递的加密字符串字段,并传递给注解。ResultVO为接⼝响应类。
DeptVO.java
1. package com.agger.springbootaopdemo.vo;
2.
3. import lombok.AllArgsConstructor;
4. import lombok.Data;
5. import lombok.NoArgsConstructor;
6.
7. /**
8. * @program: Dept
9. * @description: 部门类
10. * @author: chenhx
11. * @create: 2019-12-03 15:32
12. **/
13. @Data
14. @NoArgsConstructor
15. @AllArgsConstructor
16. public class DeptVO{
17.
18. private Integer id;
19. private String deptName;
20.
21. // ⾃⼰实现的⼀个参数,⽤来给前端传递加密字符串
22. private String encryptJson;
23.
24. }
ResultVO.java
1. package com.agger.springbootaopdemo.vo;
2.
3. import lombok.AllArgsConstructor;
4. import lombok.Data;
5. import lombok.NoArgsConstructor;
6.
7. /**
8. * @program: ResultVO
9. * @description: 响应类
10. * @author: chenhx
11. * @create: 2019-12-03 15:34
12. **/
13. @Data
14. @AllArgsConstructor
15. @NoArgsConstructor
16. public class ResultVO {
17. private Integer code;
18. private String msg;
19. private Object data;
20. }
21.
4. 定义AOP切⾯
万事具备,咱只⽋定义⼀个AOP切⾯来实现加密和解密操作了。
SecretAOPController.java
1. package com.agger.springbootaopdemo.aop;
2.
3. import com.agger.springbootaopdemo.annotation.Secret;
4. import com.agger.springbootaopdemo.utils.AESUtils;
5. import com.agger.springbootaopdemo.vo.ResultVO;
6. import com.alibaba.fastjson.JSON;
7. slf4j.Slf4j;
8. import org.apachemons.lang3.StringUtils;
9. import org.aspectj.lang.ProceedingJoinPoint;
10. import org.aspectj.lang.annotation.Around;
11. import org.aspectj.lang.annotation.Aspect;
12. import org.aspectj.lang.annotation.Pointcut;
13. import org.flect.MethodSignature;
14. import org.springframework.beans.factory.annotation.Value;
15. import org.springframework.stereotype.Component;
16.
17. import flect.Method;
18. import flect.Type;
19.
20. /**
21. * @program: SecretAOPController
22. * @description: 切⾯加密解密
23. * @author: chenhx
24. * @create: 2019-12-05 13:43
25. **/
26. @Aspect
27. @Component
28. @Slf4j
29. public class SecretAOPController {
30.
31. // 是否进⾏加密解密,通过配置⽂件注⼊(不配置默认为true)
32. @Value("${isSecret:true}")
33. boolean isSecret;
34.
35. // 定义切点,使⽤了@Secret注解的类或使⽤了@Secret注解的⽅法
36. @Pointcut("@within(com.agger.springbootaopdemo.annotation.Secret) || @annotation(com.agger.springbootaopdemo.annotation.Secret)")
37. public void pointcut(){}
38.
39. // 环绕切⾯
40. @Around("pointcut()")
41. public ResultVO around(ProceedingJoinPoint point){
42. ResultVO result = null;
43. // 获取被代理⽅法参数
44. Object[] args = Args();
45. // 获取被代理对象
46. Object target = Target();
47. // 获取通知签名
48. MethodSignature signature = (MethodSignature )Signature();
49.
50. try {
51. // 获取被代理⽅法
52. Method pointMethod = Class().Name(), ParameterTypes());
53. // 获取被代理⽅法上⾯的注解@Secret
54. Secret secret = Annotation(Secret.class);
55. // 被代理⽅法上没有,则说明@Secret注解在被代理类上
56. if(secret==null){
57. secret = Class().getAnnotation(Secret.class);
58. }
59.
60. if(secret!=null){
61. // 获取注解上声明的加解密类
62. Class clazz = secret.value();
63. // 获取注解上声明的加密参数名
64. String encryptStrName = ptStrName();
65.
66. for (int i = 0; i < args.length; i++) {
67. // 如果是clazz类型则说明使⽤了加密字符串encryptStr传递的加密参数
68. if(clazz.isInstance(args[i])){
69. Object cast = clazz.cast(args[i]); //将args[i]转换为clazz表⽰的类对象
70. // 通过反射,执⾏getEncryptStr()⽅法,获取加密数据
71. Method method = Method(getMethedName(encryptStrName));
72. // 执⾏⽅法,获取加密数据
73. String encryptStr = (String) method.invoke(cast);
74. // 加密字符串是否为空
75. if(StringUtils.isNotBlank(encryptStr)){
76. // 解密
77. String json = AESUtils.decrypt(encryptStr);
78. // 转换vo
79. args[i] = JSON.parseObject(json, (Type) args[i].getClass());
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论