欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

切面AOP注解来实现AES + RSA 加解密

程序员文章站 2024-01-20 16:10:16
前言大概2个多月前,帮一个同事一起解决使用AES和RSA非对称加密算法来对数据进行解密,需要写一个切面,然后在controller层的接口上直接添加注解,就可以对加密的数据进行解密。由于自己之前从未涉及到这块,也一直觉得切面是难啃的硬骨头,而这次经历让我对切面有了更深的理解,忽然直接切面在我面前也如猪蹄一样香,被我分崩离析了。所以这篇博客,涉及到2个技术点:AOP的around通知AES+RSA非对称加密当然里面的切面是从大神那里复制过来,然后自己也写了demo,但是复制过来之后并不能直接用...

前言

大概2个多月前,帮一个同事一起解决使用AES和RSA非对称加密算法来对数据进行解密,需要写一个切面,然后在controller层的接口上直接添加注解,就可以对加密的数据进行解密。

由于自己之前从未涉及到这块,也一直觉得切面是难啃的硬骨头,而这次经历让我对切面有了更深的理解,忽然直接切面在我面前也如猪蹄一样香,被我分崩离析了。
所以这篇博客,涉及到2个技术点:

  1. AOP的around通知
  2. AES+RSA非对称加密

当然里面的切面是从大神那里复制过来,然后自己也写了demo,但是复制过来之后并不能直接用,自己也通过不断地修改,最终实现了对加密数据地解密,解密结果再加密,以及对原数据进行加密。

我把项目详细的内容都粘贴出来了,本来是想只粘贴切面和加解密的工具类,剩下的传到github上,但是中途出了差错,切分支的时候不小心把东西全部整没了,还好还有class文件,否则我得花更多的时间恢复,这也就是为啥我类文件上的注释是2020-11-09的时间。

复制起来确实麻烦,所以我还是把github的地址粘贴出来:

https://github.com/fanhuifangGit/annotation-test.git
看着我这么辛苦的份上希望留下你的足迹和star!!!【此处应该有表情】
(这个小的demo也差不多断断续续的写了一个星期了,也算是小有收获吧!!!)

目录结构

切面AOP注解来实现AES + RSA 加解密

pom文件

<?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.1.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.fanhf</groupId>
    <artifactId>annotation-test</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>annotation-test</name>
    <description>Demo project for Spring Boot</description>


    <dependencies>
        <!-- spring-boot-starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <!-- springboot web(MVC)-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.70</version>
        </dependency>


        <!-- springboot -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--lombok插件 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!-- thymeleaf模板 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.2</version>
        </dependency>

        <!-- Base64编码需要  -->
        <dependency>
            <groupId>org.apache.directory.studio</groupId>
            <artifactId>org.apache.commons.codec</artifactId>
            <version>1.8</version>
        </dependency>

        <!--提供更多的加密、填充方式-->
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk16</artifactId>
            <version>1.46</version>
        </dependency>

        <!--aop 面向切面-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

    </dependencies>

    <!--构建工具-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <finalName>${project.artifactId}</finalName>
                    <outputDirectory>../package</outputDirectory>
                </configuration>
            </plugin>
            <!-- 跳过启动测试 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <skipTests>true</skipTests>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

配置文件:application.properties

#内置Tomcat容器配置
server.port = 8882

#基础配置
#选择配置分支,先读取系统环境变量,如果没有则默认值为 dev
spring.profiles.active = ${BASE_ADMIN:dev} 
spring.application.name = annotation-test

启动类:AnnotationTestApplication

package com.fanhf.annotationtest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AnnotationTestApplication {
    private static final Logger log = LoggerFactory.getLogger(AnnotationTestApplication.class);

    public AnnotationTestApplication() {}

    public static void main(String[] args) {
        SpringApplication.run(AnnotationTestApplication.class, args);
        log.info("启动成功!!!!!!");
    }
}

解密注解:Decrypt

package com.fanhf.annotationtest.annotation;

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

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Decrypt {
}

加密注解:Encrypt

package com.fanhf.annotationtest.annotation;

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

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Encrypt {
}

bean:

EncryptBean

package com.fanhf.annotationtest.bean;

/**
 * @author fanhf
 * @Description 加密后的bean字段
 * @date 2020-11-09 14:06
 */
public class EncryptBean {
    private String aesData;
    private String rsaData;

    public String getAesData() {
        return aesData;
    }

    public void setAesData(String aesData) {
        this.aesData = aesData;
    }

    public String getRsaData() {
        return rsaData;
    }

    public void setRsaData(String rsaData) {
        this.rsaData = rsaData;
    }
}

UserBean

package com.fanhf.annotationtest.bean;

/**
 * @author fanhf
 * @Description 要加密的bean类
 * @date 2020-11-09 12:43
 */
public class UserBean {
    private String passwd;
    private String name;
    private Integer age;

    public String getPasswd() {
        return passwd;
    }

    public void setPasswd(String passwd) {
        this.passwd = passwd;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

Result

package com.fanhf.annotationtest.bean;

import lombok.Data;

import java.io.Serializable;

/**
 * @author fanhf
 * @Description 结果返回的统一对象
 * @date 2020-11-09 12:45
 */

@Data
public class Result<T> implements Serializable {
    /**
     * 通信数据
     */
    private T data;

    /**
     * 通过静态方法获取实例
     */
    public static <T> Result<T> of(T data) {
        return new Result<>(data);
    }


    @Deprecated
    public Result() {
    }

    private Result(T data) {
        this.data = data;
    }
}

controller控制层

package com.fanhf.annotationtest.controller;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fanhf.anotation.Decrypt;
import com.fanhf.anotation.Encrypt;
import com.fanhf.result.Result;
import com.fanhf.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;


@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;
    /**
     * 我写了4个接口,前2个接口是对应切面SafetyAspect
     * 后2个接口是对应SafetyAspectPlus
     */
    /**
     * 1、对密码加密
     */
    @PostMapping(value = "/encrypt")
    @Encrypt
    public JSONObject encrypt(EncryptBean encryptBean) {
        return (JSONObject) JSONObject.toJSON(encryptBean);
    }

    /**POST
     * 2、对密码解密
     * 解密之后是用userBean来接
     */
    @PostMapping(value = "/decrypt")
    @Decrypt
    public JSONObject decrypt(UserBean userBean) {
        return (JSONObject) JSONObject.toJSON(userBean);
    }

    /**POST
     * 3、对密码解密后处理后再加密
     * 解密之后是用userBean来接
     */
    @PostMapping(value = "/ende")
    @Decrypt
    @Encrypt
    public Result<UserBean> ende(UserBean userBean) {
        UserBean userBeanNew = userService.updatePasswd(userBean);
        return Result.of(userBeanNew);
    }

    /**
     *  4、加解密
     * @date 2020/11/6 17:58
     * @return JSONObject
     * 说明:这个接口是无法正常放回加密后的数据,原因是参数和返回值不一致
     **/

    @PostMapping(value = "/endeplus")
    @Decrypt
    @Encrypt
    public JSONObject endeplus(UserBean userBean) {
        UserBean userBeanNew = userService.updatePasswd(userBean);
        return (JSONObject) JSONObject.toJSON(userBeanNew);
    }
}

UserService业务层

package com.fanhf.annotationtest.service;

import com.fanhf.controller.UserBean;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    public UserBean updatePasswd(UserBean userBean) {
        userBean.setPasswd("@"+userBean.getPasswd()+"@");
        userBean.setAge(userBean.getAge()+1);
        userBean.setName(userBean.getName()+"AOP");
        return  userBean;
    }
}

说明:我的这个service不涉及到数据库,但在真实地业务场景下应该是需要将解密后的数据进行处理,处理完之后再对数据库进行修改,之后再将处理的数据二次加密再返回给前端。我这里就简化了。

再来看看AES的加密工具类:AesUtil

package com.fanhf.annotationtest.utils;

import org.apache.tomcat.util.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Random;

/**
 * AES加、解密算法工具类
 */
public class AesUtil {
    /**
     * 加密算法AES
     */
    private static final String KEY_ALGORITHM = "AES";

    /**
     * key的长度,Wrong key size: must be equal to 128, 192 or 256
     * 传入时需要16、24、36
     */
    private static final Integer KEY_LENGTH = 16 * 8;

    /**
     * 算法名称/加密模式/数据填充方式
     * 默认:AES/ECB/PKCS5Padding
     */
    private static final String ALGORITHMS = "AES/ECB/PKCS5Padding";

    /**
     * 后端AES的key,由静态代码块赋值
     */
    public static String key;

    /**
     * 不能在代码中创建
     * JceSecurity.getVerificationResult 会将其put进 private static final Map<Provider,Object>中,导致内存缓便被耗尽
     */
    private static final BouncyCastleProvider PROVIDER = new BouncyCastleProvider();

    static {
        key = getKey();
    }

    /**
     * 获取key
     */
    public static String getKey() {
        StringBuilder uid = new StringBuilder();
        //产生16位的强随机数
        Random rd = new SecureRandom();
        for (int i = 0; i < KEY_LENGTH / 8; i++) {
            //产生0-2的3位随机数
            int type = rd.nextInt(3);
            switch (type) {
                case 0:
                    //0-9的随机数
                    uid.append(rd.nextInt(10));
                    break;
                case 1:
                    //ASCII在65-90之间为大写,获取大写随机
                    uid.append((char) (rd.nextInt(25) + 65));
                    break;
                case 2:
                    //ASCII在97-122之间为小写,获取小写随机
                    uid.append((char) (rd.nextInt(25) + 97));
                    break;
                default:
                    break;
            }
        }
        return uid.toString();
    }

    /**
     * 加密
     *
     * @param content    加密的字符串
     * @param encryptKey key值
     */
    public static String encrypt(String content, String encryptKey) throws Exception {
        //设置Cipher对象
        Cipher cipher = Cipher.getInstance(ALGORITHMS, PROVIDER);
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), KEY_ALGORITHM));

        //调用doFinal
        // 转base64
        return Base64.encodeBase64String(cipher.doFinal(content.getBytes(StandardCharsets.UTF_8)));
    }

    /**
     * 解密
     *
     * @param encryptStr 解密的字符串
     * @param decryptKey 解密的key值
     */
    public static String decrypt(String encryptStr, String decryptKey) throws Exception {
        //base64格式的key字符串转byte
        byte[] decodeBase64 = Base64.decodeBase64(encryptStr);

        //设置Cipher对象
        Cipher cipher = Cipher.getInstance(ALGORITHMS,PROVIDER);
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), KEY_ALGORITHM));

        //调用doFinal解密
        return new String(cipher.doFinal(decodeBase64));
    }
    public static void  main(String[] args) throws Exception {
        String key = getKey();
        System.out.println("key:"+key);
        String en = encrypt("asdf",key);
        System.out.println("加密后的结果:"+en);
        System.out.println(decrypt(en,key));
    }
}

再来看看RSA的加密工具类:RsaUtil

package com.fanhf.annotationtest.utils;


import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.tomcat.util.http.fileupload.ByteArrayOutputStream;

import javax.crypto.Cipher;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * RSA加、解密算法工具类
 */
@Slf4j
public class RsaUtil {

    /**
     * 加密算法AES
     */
    private static final String KEY_ALGORITHM = "RSA";

    /**
     * 算法名称/加密模式/数据填充方式
     * 默认:RSA/ECB/PKCS1Padding
     */
    private static final String ALGORITHMS = "RSA/ECB/PKCS1Padding";

    /**
     * Map获取公钥的key
     */
    private static final String PUBLIC_KEY = "publicKey";

    /**
     * Map获取私钥的key
     */
    private static final String PRIVATE_KEY = "privateKey";

    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;

    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;

    /**
     * RSA 位数 如果采用2048 上面最大加密和最大解密则须填写:  245 256
     */
    private static final int INITIALIZE_LENGTH = 1024;

    /**
     * 后端RSA的密钥对(公钥和私钥)Map,由静态代码块赋值
     */
    private static Map<String, Object> genKeyPair = new LinkedHashMap<>();

    static {
        try {
            genKeyPair.putAll(genKeyPair());
        } catch (Exception e) {
            //输出到日志文件中
            e.printStackTrace();
        }
    }

    /**
     * 生成密钥对(公钥和私钥)
     */
    private static Map<String, Object> genKeyPair() throws Exception {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGen.initialize(INITIALIZE_LENGTH);
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        Map<String, Object> keyMap = new HashMap<>(2);
        //公钥
        keyMap.put(PUBLIC_KEY, publicKey);
        //私钥
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }

    /**
     * 私钥解密
     *
     * @param encryptedData 已加密数据
     * @param privateKey    私钥(BASE64编码)
     */
    public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey) throws Exception {
        //base64格式的key字符串转Key对象
        Key privateK = KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));

        //设置加密、填充方式
        /*
            如需使用更多加密、填充方式,引入
            <dependency>
                <groupId>org.bouncycastle</groupId>
                <artifactId>bcprov-jdk16</artifactId>
                <version>1.46</version>
            </dependency>
            并改成
            Cipher cipher = Cipher.getInstance(ALGORITHMS ,new BouncyCastleProvider());
         */
        Cipher cipher = Cipher.getInstance(ALGORITHMS);
        cipher.init(Cipher.DECRYPT_MODE, privateK);

        //分段进行解密操作
        return encryptAndDecryptOfSubsection(encryptedData, cipher, MAX_DECRYPT_BLOCK);
    }

    /**
     * 公钥加密
     *
     * @param data      源数据
     * @param publicKey 公钥(BASE64编码)
     */
    public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception {
        //base64格式的key字符串转Key对象
        Key publicK = KeyFactory.getInstance(KEY_ALGORITHM).generatePublic(new X509EncodedKeySpec(Base64.decodeBase64(publicKey)));

        //设置加密、填充方式
        /*
            如需使用更多加密、填充方式,引入
            <dependency>
                <groupId>org.bouncycastle</groupId>
                <artifactId>bcprov-jdk16</artifactId>
                <version>1.46</version>
            </dependency>
            并改成
            Cipher cipher = Cipher.getInstance(ALGORITHMS ,new BouncyCastleProvider());
         */
        Cipher cipher = Cipher.getInstance(ALGORITHMS);
        cipher.init(Cipher.ENCRYPT_MODE, publicK);

        //分段进行加密操作
        return encryptAndDecryptOfSubsection(data, cipher, MAX_ENCRYPT_BLOCK);
    }

    /**
     * 获取私钥
     */
    public static String getPrivateKey() {
        Key key = (Key) genKeyPair.get(PRIVATE_KEY);
        return Base64.encodeBase64String(key.getEncoded());
    }

    /**
     * 获取公钥
     */
    public static String getPublicKey() {
        Key key = (Key) genKeyPair.get(PUBLIC_KEY);
        return Base64.encodeBase64String(key.getEncoded());
    }

    /**
     * 分段进行加密、解密操作
     */
    private static byte[] encryptAndDecryptOfSubsection(byte[] data, Cipher cipher, int encryptBlock) throws Exception {
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > encryptBlock) {
                cache = cipher.doFinal(data, offSet, encryptBlock);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * encryptBlock;
        }
        out.close();
        return out.toByteArray();
    }
}

AES和RSA的加密算法不是我写的,而是从同事那里copy的,网上也有一些关于AES和RAS算法的博客,大家可自行学习,我就不赘述了,这里只展示具体的实现。

切面1:SafetyAspect

package com.fanhf.annotationtest.aspectj;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fanhf.annotationtest.annotation.Decrypt;
import com.fanhf.annotationtest.annotation.Encrypt;
import com.fanhf.annotationtest.bean.EncryptBean;
import com.fanhf.annotationtest.utils.AesUtil;
import com.fanhf.annotationtest.utils.RsaUtil;
import org.apache.commons.codec.binary.Base64;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
 * @author fanhf
 * @Description AES + RSA 加解密AOP处理
 * @date 2020-11-09 13:47
 */
@Aspect
@Component
public class SafetyAspect {

    private static final Logger log = LoggerFactory.getLogger(SafetyAspectPlus.class);
    /**
     * 这里RSA加解密的时候需要根据公钥和私钥来进行加解密,真实的业务场景一般是从前端传过来公钥,私钥是随机获取的,我这里为了方便,就写死了。
     **/
    private static final String PRIVATEKEY = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBALwT6AsfbEV/d5Mv+fbsaxjAKDPFmJu2JnV3b03T6RX1Oailg1tawbI3boR2PC1lOjRBHoqR8GPJGNu79NmnaZ9Fq5gqUZkO/K88/BdRgiqropEqLginYiiDJm22jqdFUb2VafwLj2ou5sevtbIR6fVou881k/YRsAImsA3WH7ITAgMBAAECgYAndmnOw6YdIvS8/mkNZWfHRrJowoIV0e9Z4FiLVPZoNA8IEspwBaf0s+rNgl14DPBcfHljC+ILnetIV7S1Yoonk5epsq7BjmrB7AqXazdXLv2Cmyw8CyGjs7ShhBZPn/oAmCWIQgObptDBGrdB9bZk1s7mSr96Z4fIw9/mLXuqsQJBAPI3mnjfVcr5A5KysZezqjljYGgf5cboP/t956H+LqMLkh2DC8nbiSoOy5etWOvoarRb6t8bWc4/crCXQvvTrIkCQQDGx6ZMoLURLz8KLCRKjpUt5Dx0jmu3utjPlmfLxpiAI0VvokWpFZrc4mlib/T4zDtIDFGzGBkSC7UhJ4339Nq7AkAGv6ncKEzZpOqGkdgE5AqgIray8ACU9C+kMDPd/ZkLDe16SQZxD17Y/ySJC1lo6Ubf05fNs5Ni/b2SUgSZw6IRAkAVbDjg80TwWC4sE3vJyToMmxdk3GCBiZKKNMR08q9GyAZYtJ1bTqfE/GWtJTG6ipAtAJ7hdUxmZHqd2xxyx6G3AkASaSgCdDxd7N6O6qZs0T5V2z9PzXj1exsUwIPUtVC1EnZ+OmklUBVqRHtqw+xSwomfcKvb7zx8T9L3oCgx6okj";
    private static final String PUBLICKEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8E+gLH2xFf3eTL/n27GsYwCgzxZibtiZ1d29N0+kV9TmopYNbWsGyN26EdjwtZTo0QR6KkfBjyRjbu/TZp2mfRauYKlGZDvyvPPwXUYIqq6KRKi4Ip2IogyZtto6nRVG9lWn8C49qLubHr7WyEen1aLvPNZP2EbACJrAN1h+yEwIDAQAB";
    /**
     * Pointcut 切入点
     */
    @Pointcut("execution(public * com.fanhf.annotationtest.controller.UserController.encrypt(..))||" +
              "execution(public * com.fanhf.annotationtest.controller.UserController.decrypt(..))")

    public void safetyAspect() {}

    /**
     * 环绕通知
     */
    @Around(value = "safetyAspect()")
    public Object around(ProceedingJoinPoint pjp) {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

            assert attributes != null;

            //request对象
            HttpServletRequest request = attributes.getRequest();

            //http请求方法  post get
            String httpMethod = request.getMethod().toLowerCase();

            //method方法
            Method method = ((MethodSignature) pjp.getSignature()).getMethod();

            //method方法上面的注解
            Annotation[] annotations = method.getAnnotations();

            //方法的形参参数
            Object[] args = pjp.getArgs();

            //是否有@Decrypt
            boolean hasDecrypt = false;
            //是否有@Encrypt
            boolean hasEncrypt = false;
            for (Annotation annotation : annotations) {
                if (annotation.annotationType() == Decrypt.class) {
                    hasDecrypt = true;
                }
                if (annotation.annotationType() == Encrypt.class) {
                    hasEncrypt = true;
                }
            }

            InputStreamReader inputStreamReader = new InputStreamReader(request.getInputStream(), "UTF-8");
            StringBuilder stringBuilder;
            try (BufferedReader br = new BufferedReader(inputStreamReader)) {
                String line;
                stringBuilder = new StringBuilder();
                while ((line = br.readLine()) != null) {
                    stringBuilder.append(line);
                }
            }
            String oridata = stringBuilder.toString();
            JSONObject jsonObject = JSONObject.parseObject(oridata);
            String line = prettyJson(oridata);
            log.info("入参的切面json串:\r\n{}", line);

            if ("post".equals(httpMethod) && hasDecrypt) {
                String data = null;
                if (oridata.indexOf("data") != -1) {
                    data = jsonObject.getString("data");
                    jsonObject = JSONObject.parseObject(data);
                }
                //AES加密后的数据
                String aesKey = jsonObject.getString("aesData");
                //后端RSA公钥加密后的AES数据
                String dataString = jsonObject.getString("rsaData");
                //后端私钥解密的到AES的key

                byte[] plaintext = RsaUtil.decryptByPrivateKey(Base64.decodeBase64(dataString), PRIVATEKEY);

                String rsaData = new String(plaintext);
                log.info("解密出来的AES生成的key为:{}", rsaData);

                String originData = AesUtil.decrypt(aesKey, rsaData);
                log.info("解密出来的data数据:\r\n{}", prettyJson(originData));
                if (args.length > 0) {
                    args[0] = JSONObject.parseObject(originData, args[0].getClass());
                }
            }

            if (hasEncrypt) {
                String  aesKey = AesUtil.getKey();
                log.info("AES的key:{}", aesKey);

                String  dataString = jsonObject.toJSONString();
                String aesData = AesUtil.encrypt(dataString, aesKey);
                log.info("AesUtil加密后的数据:{}", aesData);

                String rsaData = Base64.encodeBase64String(RsaUtil.encryptByPublicKey(aesKey.getBytes(), PUBLICKEY));
                log.info("RsaUtil加密后的data数据:{}", rsaData);

                EncryptBean encryptBean = new EncryptBean();
                encryptBean.setAesData(aesData);
                encryptBean.setRsaData(rsaData);

                log.info("加密后的数据为:{}", JSONObject.toJSONString(encryptBean));
                if (args.length > 0) {
                    args[0] = JSONObject.parseObject(JSONObject.toJSONString(encryptBean), args[0].getClass());
                }
            }

            return pjp.proceed(args);

        } catch (Throwable e) {
            log.error(e.getMessage());
            return JSONObject.parseObject("{ \"加解密异常\": \"" +e.getMessage() + "\"}");
        }
    }

    public static String prettyJson(String reqJson) {
        JSONObject object = JSONObject.parseObject(reqJson);
        return JSON.toJSONString(object, new SerializerFeature[]{SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat});
    }
}   

切面2:SafetyAspectPlus

package com.fanhf.annotationtest.aspectj;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fanhf.annotationtest.annotation.Decrypt;
import com.fanhf.annotationtest.annotation.Encrypt;
import com.fanhf.annotationtest.bean.Result;
import com.fanhf.annotationtest.utils.AesUtil;
import com.fanhf.annotationtest.utils.RsaUtil;
import org.apache.commons.codec.binary.Base64;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
 * @author fanhf
 * @Description AES + RSA 加解密AOP处理
 * @date 2020-11-09 13:47
 */

@Aspect
@Component
public class SafetyAspectPlus {

    private static final Logger log = LoggerFactory.getLogger(SafetyAspectPlus.class);
    /**
     * 这里RSA加解密的时候需要根据公钥和私钥来进行加解密,真实的业务场景一般是从前端传过来公钥,私钥是随机获取的,我这里为了方便,就写死了。
     **/
    private static final String PRIVATEKEY = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBALwT6AsfbEV/d5Mv+fbsaxjAKDPFmJu2JnV3b03T6RX1Oailg1tawbI3boR2PC1lOjRBHoqR8GPJGNu79NmnaZ9Fq5gqUZkO/K88/BdRgiqropEqLginYiiDJm22jqdFUb2VafwLj2ou5sevtbIR6fVou881k/YRsAImsA3WH7ITAgMBAAECgYAndmnOw6YdIvS8/mkNZWfHRrJowoIV0e9Z4FiLVPZoNA8IEspwBaf0s+rNgl14DPBcfHljC+ILnetIV7S1Yoonk5epsq7BjmrB7AqXazdXLv2Cmyw8CyGjs7ShhBZPn/oAmCWIQgObptDBGrdB9bZk1s7mSr96Z4fIw9/mLXuqsQJBAPI3mnjfVcr5A5KysZezqjljYGgf5cboP/t956H+LqMLkh2DC8nbiSoOy5etWOvoarRb6t8bWc4/crCXQvvTrIkCQQDGx6ZMoLURLz8KLCRKjpUt5Dx0jmu3utjPlmfLxpiAI0VvokWpFZrc4mlib/T4zDtIDFGzGBkSC7UhJ4339Nq7AkAGv6ncKEzZpOqGkdgE5AqgIray8ACU9C+kMDPd/ZkLDe16SQZxD17Y/ySJC1lo6Ubf05fNs5Ni/b2SUgSZw6IRAkAVbDjg80TwWC4sE3vJyToMmxdk3GCBiZKKNMR08q9GyAZYtJ1bTqfE/GWtJTG6ipAtAJ7hdUxmZHqd2xxyx6G3AkASaSgCdDxd7N6O6qZs0T5V2z9PzXj1exsUwIPUtVC1EnZ+OmklUBVqRHtqw+xSwomfcKvb7zx8T9L3oCgx6okj";
    private static final String PUBLICKEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8E+gLH2xFf3eTL/n27GsYwCgzxZibtiZ1d29N0+kV9TmopYNbWsGyN26EdjwtZTo0QR6KkfBjyRjbu/TZp2mfRauYKlGZDvyvPPwXUYIqq6KRKi4Ip2IogyZtto6nRVG9lWn8C49qLubHr7WyEen1aLvPNZP2EbACJrAN1h+yEwIDAQAB";
    /**
     * Pointcut 切入点
     */
    @Pointcut("execution(public * com.fanhf.annotationtest.controller.UserController.ende(..))")
    public void safetyAspectPlus() {}
    /**
     * 环绕通知
     */
    @Around(value = "safetyAspectPlus()")
    public Object around(ProceedingJoinPoint pjp) {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

            assert attributes != null;

            //request对象
            HttpServletRequest request = attributes.getRequest();

            //http请求方法  post get
            String httpMethod = request.getMethod().toLowerCase();

            //method方法
            Method method = ((MethodSignature) pjp.getSignature()).getMethod();

            //method方法上面的注解
            Annotation[] annotations = method.getAnnotations();

            //方法的形参参数
            Object[] args = pjp.getArgs();

            //是否有@Decrypt
            boolean hasDecrypt = false;
            //是否有@Encrypt
            boolean hasEncrypt = false;
            for (Annotation annotation : annotations) {
                if (annotation.annotationType() == Decrypt.class) {
                    hasDecrypt = true;
                }
                if (annotation.annotationType() == Encrypt.class) {
                    hasEncrypt = true;
                }
            }

            InputStreamReader inputStreamReader = new InputStreamReader(request.getInputStream(), "UTF-8");
            StringBuilder stringBuilder;
            try (BufferedReader br = new BufferedReader(inputStreamReader)) {
                String line;
                stringBuilder = new StringBuilder();
                while ((line = br.readLine()) != null) {
                    stringBuilder.append(line);
                }
            }
            String oridata = stringBuilder.toString();
            JSONObject jsonObject = JSONObject.parseObject(oridata);
            String line = prettyJson(oridata);
            log.info("入参的切面json串:\r\n{}", line);

            if ("post".equals(httpMethod) && hasDecrypt) {

                /*
                 * 由于通过了Result加密后多了个data,所以再次加密后需要获取data的value后再进行加密
                 * {
                 *     "data": {
                 *         "rsaData": "FkvZ+vCiyJePoaXbZDfw++Jfgk7gJNAE9piaPOebH0YcaAF70q3GWracDSSiXG0FFKmzV/MGadl5rIyg3DA+Z9/ETDRCr6Z5SYSfL5FDmL0jTUIMxErvPEjYsTIk9MTur7J1/p4wVsTxkZprXUoc3fXdyshi6cM2kC+KuNxwGjA=",
                 *         "aesData": "K8pocSiGFUZYmmFgmFOD6iwcBBi8BBBclSyePKPd+pDUft4B/wmB5nk4sd9CWNCR3F36X2JVrOrqM2XIm+kX8Q=="
                 *     }
                 * }
                 **/

                String data = null;
                if (oridata.indexOf("data") != -1) {
                    data = jsonObject.getString("data");
                    jsonObject = JSONObject.parseObject(data);
                }
                //AES加密后的数据
                String aesKey = jsonObject.getString("aesData");

                //后端RSA公钥加密后的AES数据
                String dataString = jsonObject.getString("rsaData");

                //后端私钥解密的到AES的key
                byte[] plaintext = RsaUtil.decryptByPrivateKey(Base64.decodeBase64(dataString), PRIVATEKEY);

                String rsaData = new String(plaintext);
                log.info("解密出来的AES生成的key为:{}", rsaData);

                String originData = AesUtil.decrypt(aesKey, rsaData);
                log.info("解密出来的data数据:\r\n{}", prettyJson(originData));

                if (args.length > 0) {
                    args[0] = JSONObject.parseObject(originData, args[0].getClass());
                }
            }

            Object object = pjp.proceed(args);
            if (hasEncrypt) {
               String  aesKey = AesUtil.getKey();
                log.info("AES的key:{}", aesKey);

                String  dataString = JSONObject.toJSONString(object);
                log.info("需要解密处理后再加密json数据:\r\n{}", prettyJson(dataString));

                dataString = JSONObject.parseObject(dataString).getString("data");
                log.info("把data对应的value提取出来的originData:\r\n{}", prettyJson(dataString));

                String aesData = AesUtil.encrypt(dataString, aesKey);
                log.info("AesUtil加密后的数据:{}", aesData);

                String rsaData = Base64.encodeBase64String(RsaUtil.encryptByPublicKey(aesKey.getBytes(), PUBLICKEY));
                log.info("RsaUtil加密后的data数据:{}", rsaData);

                String originData = "{\"rsaData\": \"" + rsaData + "\",\"aesData\": \"" + aesData + "\"}";
                log.info("未Result化,返回给前端的加密数据为:\r\n{}", prettyJson(originData));
                object = Result.of(JSONObject.parseObject(originData, Object.class));
            }

            return object;
        } catch (Throwable e) {
            log.error(e.getMessage());
            return Result.of("加解密异常:\n\t" + e.getMessage());
        }
    }

    public static String prettyJson(String reqJson) {
        JSONObject object = JSONObject.parseObject(reqJson);
        return JSON.toJSONString(object, new SerializerFeature[]{SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat});
    }
}

这两个切面的区别比较少,不同的在返回值和对data的处理,同学们可以自己copy到开发工具中debug模式看看具体的区别。

测试

我是用的postman来调用的:
加密(调用的是切面1:SafetyAspect)
切面AOP注解来实现AES + RSA 加解密

拿着加密的数据去解密(调用的是切面1:SafetyAspect)
切面AOP注解来实现AES + RSA 加解密
解密后的数据处理后再加密(调用的是切面1:SafetyAspectPlus)
切面AOP注解来实现AES + RSA 加解密
再对SafetyAspectPlus加密的结果用切面SafetyAspect来解密
切面AOP注解来实现AES + RSA 加解密
和第一次加密后用切面SafetyAspect解密的结果是一样的。
对应的日志:
切面AOP注解来实现AES + RSA 加解密
切面AOP注解来实现AES + RSA 加解密
切面AOP注解来实现AES + RSA 加解密
这3张日志的截图是刚才在postman执行的日志纪录,是完整的日志记录。

到这里这篇博客就基本完成了,如果有更好的办法或建议欢迎留下足迹。

----------------------与君共勉------------------------

本文地址:https://blog.csdn.net/fhf2424045058/article/details/109571882