在springboot中用自定义注解+AOP实现返回值统一加密

需求:

根据数据安全需求,需要把统一返回类中的data中的数据进行统一加密,然后由前端进行解密并把参数渲染到页面。

准备工作:

因为项目中很多接口都需要进行返回值数据加密,则考虑使用自定义注解+aop 切面技术实现。
只是对数据进行简单加密所以考虑使用des 对称加密,需要前端人员与后台开发人员商量好密钥即可满足需求。

自定义加密注解


import java.lang.annotation.*;

/**
 * 返回参数加密注解
 */
 
// 可以作用在类上和方法上
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DESRespondSecret {

    boolean validated() default true;
}

切面实现类

import com.alibaba.fastjson.JSON;
import com.test.apitest.annotation.DESRespondSecret;
import com.test.apitest.utils.EncryptUtil;
import com.test.apitest.vo.BaseVo;
import org.apache.commons.collections.CollectionUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.*;

@Component
@Aspect
//规定AOP 的执行顺序  
//@Order(1)   多个aop 时可通过该注解来确定执行顺序
public class DESResponseSecretAspect {

    // 定义切点,使用了@DESRespondSecret注解的类 或 使用了@DESRespondSecret注解的方法
    /**
     * 切点,方法上有注解或者类上有注解
     *  拦截类或者是方法上标注注解的方法
     */
    @Pointcut("@within(com.test.apitest.annotation.DESRespondSecret) || @annotation(com.test.apitest.annotation.DESRespondSecret)")
    public void pointCut(){}


    @Around("pointCut()")
    public Object after(ProceedingJoinPoint joinPoint) throws Throwable {
        Object rvt = joinPoint.proceed(joinPoint.getArgs());
        return rvt;
    }

    /**
     * 与After的区别在于AfterReturning只有在方法执行成功的之后才会被织入,如果After和
     * AfterReturning同时存在于一个文件中,谁写在前面谁先运行
     * @param joinpoint
     */
    @AfterReturning(pointcut="execution(public * com.test.apitest.controller.*.*(..))", returning="rvt")
    public Object log(JoinPoint joinpoint, Object rvt) {

        // 获取被代理对象
        Object target = joinpoint.getTarget();
        // 获取通知签名  获取方法方法上的DESRespondSecret注解
        MethodSignature signature = (MethodSignature) joinpoint.getSignature();
        try {
            // 获取被代理方法
            Method pointMethod = target.getClass().getMethod(signature.getName(), signature.getParameterTypes());
            // 获取被代理方法上面的注解@DESRespondSecret
            DESRespondSecret secret = pointMethod.getAnnotation(DESRespondSecret.class);
            // 被代理方法上没有,则说明@DESRespondSecret注解在被代理类上
            if (secret == null) {
                secret = target.getClass().getAnnotation(DESRespondSecret.class);
            }
            //类上也没有 直接返回
            if(secret == null){
                return rvt;
            }
            // 如果有,并且值为false,则不进行加密
            if (secret != null && !secret.validated()) {
                return rvt;
            }else {
               BaseVo baseVo1 = (BaseVo) rvt;
                // 获取返回值json字符串
                Object data = baseVo1.getData();
                if(null != data){
                    String jsonString = JSON.toJSONString(data);
                    // 加密
                    String s = EncryptUtil.encrypt(jsonString);
                    baseVo1.setData(s);
                }
                return baseVo1;

            }

        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return rvt;
    }

}

BaseVo类 公共返回实体类

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;

/**
 * @ClassName: BaseVo
 * @Description: 基础视图对象
 * @author:
 * @date:
 */
@Data
@ApiModel(value = "基础视图对象")
public class BaseVo<T> implements Serializable{

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "提示码")
    private String code;
    @ApiModelProperty(value = "提示内容")
    private String message;
    @ApiModelProperty(value = "数据")
    private Object data;

    @ApiModelProperty(value = "主链路id")
    protected String transactionId;
    @ApiModelProperty(value = "相关链路id")
    protected String spanId;
    @ApiModelProperty(value = "服务信息")
    protected String serverInfo;

    public BaseVo<T> setCode(String code) {
        this.code = code;
        return this;
    }

    public BaseVo<T> setMessage(String message) {
        this.message = message;
        return this;
    }

    public BaseVo<T> setData(Object data) {
        this.data = data;
        return this;
    }
   
}


EncryptUtil des对称加密算法


import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import java.security.SecureRandom;

/**
 * des 对称加密算法
 */
public class EncryptUtil {
	public static String secretKeyB = "aa.test.business.apitest@2023";
	private final static String DES = "DES";

	//加密
	public static String encrypt(String data) {
		byte[] bt;
		try {
			bt = encrypt(data.getBytes(), secretKeyB.getBytes());
			//加密
			String strs = Base64.getEncoder().encodeToString(bt);
			return strs;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	//解密
	public static String decrypt(String data) {
		if (data == null)
			return null;
		byte[] buf;
		byte[] bt;
		try {
			//解密
			buf = Base64.getDecoder().decode(data);
			bt = decrypt(buf, secretKeyB.getBytes());
			return new String(bt);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	private static byte[] encrypt(byte[] data, byte[] key) throws Exception {
		// 生成可信任的随机数源
		SecureRandom sr = new SecureRandom();
		// 从原始密钥数据创建DESKeySpec对象
		DESKeySpec dks = new DESKeySpec(key);
		// 创建密钥工厂,然后用它把DESKeySpec转换成SecretKey对象
		SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
		SecretKey securekey = keyFactory.generateSecret(dks);
		// Cipher对象实际完成加密操作
		Cipher cipher = Cipher.getInstance(DES);
		// 用密钥初始化Cipher对象
		cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);
		return cipher.doFinal(data);
	}

	private static byte[] decrypt(byte[] data, byte[] key) throws Exception {
		// 生成可信任的随机数源
		SecureRandom sr = new SecureRandom();
		// 从原始密钥数据创建DESKeySpec对象
		DESKeySpec dks = new DESKeySpec(key);
		// 创建密钥工厂,然后用它把DESKeySpec转换成SecretKey对象
		SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
		SecretKey securekey = keyFactory.generateSecret(dks);
		// Cipher对象实际完成解密操作
		Cipher cipher = Cipher.getInstance(DES);
		// 用密钥初始化Cipher对象
		cipher.init(Cipher.DECRYPT_MODE, securekey, sr);
		return cipher.doFinal(data);
	}
}

使用方法

在类上添加注解,整个类中的方法返回值都会加密:
在这里插入图片描述
在方法上添加注解,只有该方法的返回值加密:
在这里插入图片描述
如果在类上添加了注解,在该类中的方法上添加了@DESRespondSecret(validated = false) 则该方法的返回值则不会被加密

返回值加密返回的实例

接口返回值加密后返回图片

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值