Springboot系列:Springboot+AOP+注解实现字段AES+Base64加解密

AOP实现AES+BASE64加解密

场景如下:
需要对数据库存储的字段,进行加解密的处理。如果都直接写代码的话,那么代码回冗余很多,所以使用AOP+注解去实现。让代码简洁,方便

具体实现如下:

1、依赖
<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>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.20</version>
            <scope>compile</scope>
        </dependency>
2、相关注解
package com.walker.aop.fieldAop.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* author:walker
* time: 2023/12/8
* description:  支持属性加密注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SupportFieldEncrypt {
}

package com.walker.aop.fieldAop.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* author:walker
* time: 2023/12/8
* description: 支持属性解密注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SupportFieldDecrypt {
}

package com.walker.aop.fieldAop.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


/**
* author:walker
* time: 2023/12/8
* description: 属性加密
*/
@Target(value = ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldEncrypt {
    /**
    * 具体参数查看FieldEncryptTypeEnums枚举
     * 默认使用AES+BASE64加密
    */
    int type() default 1;
}

package com.walker.aop.fieldAop.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* author:walker
* time: 2023/12/8
* description:  属性解密
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldDecrypt {
    int type() default 1;
}

3、枚举

目前只整合了AES加密的,后续如果需要,可以自己整合

package com.walker.aop.fieldAop.enums;

public enum FieldEncryptTypeEnums {
    AES_BASE64(1,"AES+BASE64");
    private Integer type;
    private String description;

    FieldEncryptTypeEnums(Integer type, String description) {
        this.type = type;
        this.description = description;
    }

    public Integer getType() {
        return type;
    }

    public void setType(Integer type) {
        this.type = type;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

4、AES加解密工具类
package com.walker.aop.fieldAop;

import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;



import java.nio.charset.StandardCharsets;
/**
* author:walker
* time: 2023/12/7
* description:  AES加解密工具
*/
public class MyAesUtils {

//    密钥,可以自己修改,当然这里可以不在这里写死,但是这里为了方便,就直接写死了
    private final static String AES_KEY="qu6ciLzMME7slNxj";
    private static final AES aes = SecureUtil.aes(AES_KEY.getBytes(StandardCharsets.UTF_8));

    public static String encrypt(String data){
        return Base64.encode(aes.encrypt(data));
    }

    public static String decrypt(String data){
        return aes.decryptStr(Base64.decode(data));
    }

    public static void main(String[] args) {
        String hello = encrypt("hello");
        System.out.println(hello);
        String decrypt = decrypt(hello);
        System.out.println(decrypt);
    }
}

5、切面类
package com.walker.aop.fieldAop.aspect;

import cn.hutool.core.util.ReflectUtil;
import com.walker.aop.fieldAop.enums.FieldEncryptTypeEnums;
import com.walker.aop.fieldAop.MyAesUtils;
import com.walker.aop.fieldAop.annotations.FieldDecrypt;
import com.walker.aop.fieldAop.annotations.FieldEncrypt;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;

@Slf4j
//1、使用切面注解
@Aspect
@Component
public class FieldAopAspect {

//    2、这里的引用路径需要根据自己项目的包去调整
    @Pointcut("@annotation(com.walker.aop.fieldAop.annotations.SupportFieldEncrypt)")
    public void supportFieldEncrypt() {};

    @Pointcut("@annotation(com.walker.aop.fieldAop.annotations.SupportFieldDecrypt)")
    public void supportFieldDecrypt() {};

    /**
    * author:walker
    * time: 2023/12/8
    * description:  前置通知 加密处理
    */
//    3、Before代表前置通知
    @Before("supportFieldEncrypt()")
    public void fieldEncryptProcessor(JoinPoint joinPoint){
        //获取参数
        Object[] args = joinPoint.getArgs();

        if(args==null||args.length==0){
            return;
        }
        for (Object arg : args) {
            Class<?> aClass = arg.getClass();
//            获取属性
            Field[] fields = aClass.getDeclaredFields();
            for (Field field : fields) {
//                获取注解
                Annotation[] annotations = field.getAnnotations();
                for (Annotation annotation : annotations) {
//                    判断是否有FieldEncrypt注解
                    if(annotation.annotationType().equals(FieldEncrypt.class)){
                        FieldEncrypt fieldEncrypt= (FieldEncrypt) annotation;
                        int type = fieldEncrypt.type();
                        if(type== FieldEncryptTypeEnums.AES_BASE64.getType()){
//                            如果有注解,则进行加密处理,并使用反射去设置值
                            Object fieldValue = ReflectUtil.getFieldValue(arg, field);
                            if(fieldValue==null) return;
                            String encrypt = MyAesUtils.encrypt(String.valueOf(fieldValue));
                            System.out.println("加密后结果:"+encrypt);
                            ReflectUtil.setFieldValue(arg,field,encrypt);
                        }
                    }
                }
            }
        }
    }


    /**
    * 解密注解
    */
//    返回后通知
    @AfterReturning(value = "supportFieldDecrypt()",returning = "arg")
    public void fieldDecryptProcess(JoinPoint joinPoint, Object arg){
        if(arg==null) return;
//        arg:代表的是返回的结果
        Class<?> aClass = arg.getClass();
//        获取属性
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {
            Annotation[] annotations = field.getAnnotations();
            for (Annotation annotation : annotations) {
//                判断属性是否有FieldDecrypt注解
                if(annotation.annotationType().equals(FieldDecrypt.class)){
                    FieldDecrypt fieldDecrypt= (FieldDecrypt) annotation;
                    int type = fieldDecrypt.type();
//                    如果存在注解,则进行解密并且重新设置回对象中
                    if(type==FieldEncryptTypeEnums.AES_BASE64.getType()){
                        Object fieldValue = ReflectUtil.getFieldValue(arg, field);
                        System.out.println("解密前结果:"+fieldValue);
                        if(fieldValue==null) return;
                        String decrypt = MyAesUtils.decrypt(String.valueOf(fieldValue));
                        System.out.println("解密后结果:"+decrypt);
                        ReflectUtil.setFieldValue(arg,field,decrypt);
                    }
                }
            }
        }
    }
}

6、测试
  • 测试实体类
package com.walker.aop.fieldAop;

import com.walker.aop.fieldAop.annotations.FieldDecrypt;
import com.walker.aop.fieldAop.annotations.FieldEncrypt;
import lombok.Data;

@Data
public class FieldAopDemo {
    @FieldEncrypt
    @FieldDecrypt
    private String name;
}

  • controller
package com.walker.aop.fieldAop;

import com.walker.aop.fieldAop.annotations.SupportFieldDecrypt;
import com.walker.aop.fieldAop.annotations.SupportFieldEncrypt;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/fieldAOP")
public class FieldAOPController {

    @SupportFieldEncrypt
    @GetMapping("/encrypt")
    public void encrypt(FieldAopDemo demo){
        System.out.println(demo);
        return;
    }

    @SupportFieldDecrypt
    @GetMapping("/decrypt")
    public FieldAopDemo decrypt(FieldAopDemo demo){
        System.out.println(demo);
        String name = demo.getName();
        String encrypt = MyAesUtils.encrypt(name);
        System.out.println(encrypt);
        demo.setName(encrypt);
        return demo;
    }
}

之后使用postman调用结果测试:
加密测试:
http://localhost:8080/fieldAOP/encrypt?name=hello
image.png

解密测试:
http://localhost:8080/fieldAOP/decrypt?name=hello
image.png

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
可以使用 RedisTemplate 实现分布式锁,具体实现步骤如下: 1. 定义一个自定义注解,用于标识需要加锁的方法。 2. 在方法执行前获取锁,执行后释放锁。 3. 使用 RedisTemplate 操作 Redis,实现分布式锁。 下面是示例代码: ```java @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface DistributedLock { String key(); long expire() default 30000; } @Component @Aspect public class DistributedLockAspect { @Autowired private RedisTemplate<String, String> redisTemplate; @Around("@annotation(distributedLock)") public Object doAround(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) throws Throwable { String key = distributedLock.key(); long expire = distributedLock.expire(); // 获取锁 boolean locked = false; String lockKey = "lock:" + key; String lockValue = UUID.randomUUID().toString(); try { locked = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, expire, TimeUnit.MILLISECONDS); if (!locked) { throw new RuntimeException("获取锁失败"); } // 执行方法 return joinPoint.proceed(); } finally { // 释放锁 if (locked) { String value = redisTemplate.opsForValue().get(lockKey); if (lockValue.equals(value)) { redisTemplate.delete(lockKey); } } } } } ``` 在需要加锁的方法上加上 @DistributedLock 注解,指定锁的 key 和过期时间即可。 例如: ```java @Service public class UserService { @Autowired private UserDao userDao; @DistributedLock(key = "user:update:${#userId}") public void updateUser(long userId, String name) { userDao.updateUser(userId, name); } } ``` 这样就可以实现分布式锁了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WalkerShen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值