【java开发技术积累篇】之springboot项目api接口优雅的加解密方式和日志保存

392 阅读8分钟

在后端开发当中,有些比较敏感的,数据比较重要的或者是容易被攻击的接口,作为一个优秀的后端人员,肯定是需要做接口加密的,接口加密方式有很多种,本篇文章我主要讲解注解的方式进行加解密。在此将自己的经验描述一下,让各位后辈能学习到东西。。。

一、什么是api接口加解密

写一个接口,很简单,但是想写好一个优美的接口却不简单。项目必须也要写得能有多牢固就有多牢固,不能稍微出点事故,就扛不住了。这不是一个合格的程序员该做的事。接口加解密我认为是必须要懂的一项核心技能。因为接口一旦加了密,那么安全性将会提高很多,虽然不可能牢不可摧,但是基本的防御能力是有了。普通小白的加解密,可能就是在每个方法内部,都进行单独的加解密,这样无疑是很菜的做法,优雅的做法是应用AOP切面加解密+注解的方式进行。

二、AOP切面 + 加解密注解 + 日志保存(请求参数加密+响应内容加密)

首先讲解下大致的思路,好让学习者能看懂我的逻辑是怎样的。
1、我们先定义2个注解类,分别是请求加密@DecryptRequest,响应加密@EncryptionResponse
2、我们要定义个抽象类实现mq接口类(mq用作异步保存请求日志的作用)
AbstactWebLogAspect implements ISendLogMq
该抽象类主要是作为aop切面的方式做加解密的数据解析和日志保存的功能,aop的几个注解可以了解一下,无非就是在调用方法的前后进行拦截处理 (@Around、@Before、@After、@AfterReturning、@AfterThrowing)
3、我们需要在不同的模块当中,新增日志切面类WebLogAspect,再继承该切面日志抽象类,使其拥有该抽象类的所有方法,并且定义需要被aop切入的controller位置。
4、当以上的过程都做好了之后,基本上在接口方法的上面使用请求加密@DecryptRequest,响应加密@EncryptionResponse就可以了。

三、废话不多说,直接上代码

先看controller类,SecurityQueryDto是通用的请求参数类,前端传的参数就是这个实体类。OrderBuyDto这个实体参数类目前是空的,不需要前端传,但是当方法被aop切入后,最终会从SecurityQueryDto拿到真实的数据进行解密后,最后赋值到OrderBuyDto这个实体类中,所以我们在controller层中可以直接拿到OrderBuyDto里面的参数来走逻辑。

@DecryptRequest
@EncryptionResponse
@ApiOperation("房间送礼物")
@PostMapping(value = "/addRoomPkGift")
public ResponseResult addRoomPkGift(@RequestBody @Validated
                                                SecurityQueryDto model,
                                                OrderBuyDto orderBuyDto) {
    ResponseResult responseResult = ResponseResult.error();
    //执行各自的逻辑。。。
    return responseResult;
}
import io.swagger.annotations.ApiModelProperty;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;

/**
 * 加密数据实体
 */
@Data
public class SecurityQueryDto {

    @ApiModelProperty(value = "数据")
    @NotBlank
    private String data;

    @ApiModelProperty(value = "0 不加密 1 rsa 加密  2 aes 加密")
    @NotNull
    private Integer security;

    @ApiModelProperty(value = "aes 密码")
    private String aesKey;
}

@DecryptRequest注解类,主要标记该接口请求发送的参数需要加密

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
        
/**
 * 加密
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DecryptRequest {
    String value() default "1";
}

@EncryptionResponse注解类,主要标记该接口响应的数据需要被加密

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

/**
 * 加密
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptionResponse {
    String value() default "1";
}

切面日志抽象类AbstactWebLogAspect
这里的主要内容就是接口加解密的核心和正常日志和错误日志的保存,一定要认真的看

import com.alibaba.fastjson.JSON;
import com.google.common.base.Throwables;
import com.inno.leyin.common.component.AbstactRSAUtil;
import com.inno.leyin.common.constant.CommonConstant;
import com.inno.leyin.common.dto.ResponseResult;
import com.inno.leyin.common.dto.SecurityQueryDto;
import com.inno.leyin.common.encryption.DecryptRequest;
import com.inno.leyin.common.encryption.EncryptionResponse;
import com.inno.leyin.common.exception.ApiException;
import com.inno.leyin.common.log.DbCollectionNames;
import com.inno.leyin.common.log.SysLogIgnore;
import com.inno.leyin.common.util.Base64Utils;
import com.inno.leyin.common.util.IpAdrressUtil;
import com.inno.leyin.common.util.JsonUtil;
import com.inno.leyin.common.util.StringUtil;
import com.inno.leyin.common.vo.log.ISendLogMq;
import com.inno.leyin.common.vo.log.LeyinLog;
import com.inno.leyin.common.vo.log.LeyinLogError;
import com.inno.leyin.common.vo.log.LeyinLogEvent;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.reflect.MethodSignature;
import org.bouncycastle.util.encoders.UTF8;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.Objects;
import org.aspectj.lang.annotation.Before;

/**
 * 控制层统一日志处理
 */
public abstract class AbstactWebLogAspect implements ISendLogMq {

    //获取日志信息类
    private static final Logger LOGGER = LoggerFactory.getLogger(AbstactWebLogAspect.class);

    private static final ThreadLocal<Long> START_TIME_MILLIS = new ThreadLocal<>();//记录整个接口请求到响应所消耗的时间

    public abstract void weblogAspect();

    @Value("${spring.application.name}")
    private String applicationName;

    @Autowired
    private AbstactRSAUtil leyinRSAUtil;

    // 是否开启日志存储
    @Value("${sysLog.pushMq.enabled}")
    private Boolean logEnabled;

    @Before(value = "weblogAspect()")
    public void doBefore(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        HttpServletRequest httpServletRequest = null;
        for (int i = 0; i < args.length; i++) {
            if (args[i] instanceof HttpServletResponse) {
                args[i] = null;
            }
            if (args[i] instanceof HttpServletRequest) {
                args[i] = null;
            }
            if (args[i] instanceof MultipartFile) {
                args[i] = null;
            }
            if (args[i] instanceof MultipartFile[]) {
                args[i] = null;
            }
        }
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        // request 处理解密
        DecryptRequest decryptRequest = method.getAnnotation(DecryptRequest.class);
        if (!Objects.isNull(decryptRequest)) {
            if (args[0] instanceof SecurityQueryDto) {
                // 解密请求
                try {
                    SecurityQueryDto securityQueryDto = (SecurityQueryDto) args[0];
                    // 如果接口加密不能传未加密的数据
                    if (securityQueryDto.getSecurity() < 1) {
                        throw new ApiException("request error", 41400);
                    }
                    String dataStr = leyinRSAUtil.decrypt(securityQueryDto);
                    // 复制到加密接口的第二个实体类参数
                    BeanUtils.copyProperties(JSON.parseObject(dataStr, args[1].getClass()), args[1]);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        //LOGGER.info("请求ClassName:{},Method:{}, Params:{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), JsonUtil.toJsonString(args));
        START_TIME_MILLIS.set(System.currentTimeMillis());
    }

    @AfterReturning(pointcut = "weblogAspect()", returning = "result")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        SysLogIgnore annotation = method.getAnnotation(SysLogIgnore.class);
        if (!Objects.isNull(annotation)) {
            // 忽略日志
            //LOGGER.debug("log ignore");
        } else {
            // 接收到請求,記錄請求內容
            HttpServletRequest request = null;
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                request = attributes.getRequest();
            } else {
                LOGGER.error("servletRequestAttributes attributes is null");
            }

            Long useTime = (System.currentTimeMillis() - START_TIME_MILLIS.get());
            String args = JsonUtil.toJsonString(joinPoint.getArgs());
            String responseStr = JsonUtil.toJsonString(result);
            String url = "";
            String httpMethod = "";
            String queryString = "";
            if (null != request) {
                url = request.getRequestURI();
                httpMethod = request.getMethod();
                if(StringUtil.isEmpty(request.getQueryString())){
                    queryString = request.getQueryString();
                }
            }
            LOGGER.info("request {} url:{}, queryString:{}, Params:{}, Result:{},time:{}ms", httpMethod, url, queryString, args, responseStr, useTime);
            START_TIME_MILLIS.remove();
            handleLogInfo(request, joinPoint.getSignature(), joinPoint.getArgs(), result, useTime);
        }
        // response 处理加密
        EncryptionResponse encryptionResponse = method.getAnnotation(EncryptionResponse.class);
        if (!Objects.isNull(encryptionResponse)) {
            if (result instanceof ResponseResult) {
                // 加密返回
                try {
                    ResponseResult responseResult = (ResponseResult) result;
                    if (null != responseResult.getData()){
                        //data不为null才去加密
                        leyinRSAUtil.encrypt(responseResult, CommonConstant.SECURITY_TYPE_AES);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 获取当前登录会员
     */
    public abstract String getSessionUserId();

    /**
     * 保存本次的接口请求和响应内容日志
     * @param request
     * @param signature
     * @param args
     * @param response
     * @param time
     */
    protected void handleLogInfo(HttpServletRequest request, Signature signature, Object args, Object response, Long time) {
        if (!logEnabled) {
            // 关闭日志记录
            return;
        }
        LeyinLog vo = new LeyinLog();
        if (null != request) {
            String platform = request.getHeader("platform");
            String deviceId = request.getHeader("deviceId");
            String version = request.getHeader("version");
            String reqId = request.getHeader("X-Request-Id");
            String channelPackage = request.getHeader("channelPackage");
            String ipAdrress = IpAdrressUtil.getIpAddr(request);
            String packageName = request.getHeader("packageName");
            try{
                if (StringUtil.isNotEmpty(packageName)){
                    //base64解密
                    byte[] bytes = Base64Utils.decode(packageName);
                    packageName = new String(bytes, Charset.forName("UTF-8"));
                }
            }catch (Exception e){
                //packageName = "leyin_log packageName decode error";
                e.printStackTrace();
            }
            String appName = request.getHeader("appName");

            LOGGER.info("header --> platform:{}, deviceId:{}, version:{},ipAdrress:{},channelPackage:{},packageName:{},appName:{}", platform, deviceId, version,ipAdrress, channelPackage,packageName,appName);
            String token = request.getHeader("accessToken");
            if (StringUtil.isNotEmpty(token)) {
                vo.setUserId(getSessionUserId());
            }
            String ticketId = request.getHeader("ticketId");
            if(StringUtil.isNotEmpty(ticketId)){
                vo.setUserId(getSessionUserId());
            }
            vo.setPlatform(platform);
            vo.setDeviceId(deviceId);
            vo.setVersion(version);
            vo.setUrl(request.getRequestURI());
            vo.setIpAddress(ipAdrress);
            vo.setReqId(reqId);
            vo.setChannelPackage(channelPackage);
            vo.setPackageName(packageName);
            vo.setAppName(appName);
        }
        vo.setMethod(signature.getDeclaringTypeName() + ":" + signature.getName());
        vo.setRequest(args);
        vo.setResponseTime(time);
        vo.setResponse(response);
        vo.setServiceName(applicationName);
        vo.setCreateTime(new Date());
        //mq异步保存请求日志
        sendLogEvent(new LeyinLogEvent(DbCollectionNames.LEYIN_LOG, vo));
    }

    /**
     * 保存请求错误日志
     * @param request
     * @param signature
     * @param args
     * @param errorMessage
     * @param errorStack
     * @param errorType
     */
    protected void handleLogError(HttpServletRequest request, Signature signature, Object args, String errorMessage, String errorStack,String errorType) {
        if (!logEnabled) {
            // 关闭日志记录
            return;
        }
        LeyinLogError vo = new LeyinLogError();
        if (null != request) {
            String platform = request.getHeader("platform");
            String deviceId = request.getHeader("deviceId");
            String version = request.getHeader("version");
            String reqId = request.getHeader("X-Request-Id");
            String packageName = request.getHeader("packageName");
            try{
                if (StringUtil.isNotEmpty(packageName)){
                    //base64解密
                    byte[] bytes = Base64Utils.decode(packageName);
                    packageName = new String(bytes, Charset.forName("UTF-8"));
                }
            }catch (Exception e){
                //packageName = "leyin_log_error packageName decode error";
                e.printStackTrace();
            }
            String appName = request.getHeader("appName");
            LOGGER.info("header --> platform:{}, deviceId:{}, version:{},packageName:{},appName:{}", platform, deviceId, version,packageName,appName);
            String token = request.getHeader("accessToken");
            if (StringUtil.isNotEmpty(token)) {
                vo.setUserId(getSessionUserId());
            }
            String ticketId = request.getHeader("ticketId");
            if(StringUtil.isNotEmpty(ticketId)){
                vo.setUserId(getSessionUserId());
            }
            vo.setPlatform(platform);
            vo.setDeviceId(deviceId);
            vo.setVersion(version);
            vo.setUrl(request.getRequestURI());
            vo.setReqId(reqId);
            vo.setPackageName(packageName);
            vo.setAppName(appName);
        }
        vo.setMethod(signature.getDeclaringTypeName() + ":" + signature.getName());
        vo.setRequest(args);
        vo.setError(errorMessage);
        vo.setErrorStack(errorStack);
        vo.setErrorType(errorType);
        vo.setServiceName(applicationName);
        vo.setCreateTime(new Date());
        //mq异步保存
        sendLogEvent(new LeyinLogEvent(DbCollectionNames.LEYIN_LOG_ERROR, vo));
    }

    /**
     * 接口请求后,接口异常后处理
     * @param joinPoint
     * @param ex
     */
    @AfterThrowing(value = "weblogAspect()", throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
        // 接收到請求,記錄請求內容
        HttpServletRequest request = null;
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            request = attributes.getRequest();
        } else {
            LOGGER.error("servletRequestAttributes attributes is null");
        }
        LOGGER.error("异常 :", ex);
        String errorsStarck = Throwables.getStackTraceAsString(ex);//堆栈信息
        String errorMessage = ex.getMessage();//错误信息
        String args = JsonUtil.toJsonString(joinPoint.getArgs());//参数
        String errorType = ex.getClass().getName();
        //保存错误日志
        handleLogError(request, joinPoint.getSignature(), args, errorMessage, errorsStarck,errorType);
        START_TIME_MILLIS.remove();
    }
}

各自不同模块的实际类WebLogAspect,必须继承切面日志抽象类

import com.inno.leyin.common.config.AbstactWebLogAspect;
import com.inno.leyin.common.enums.MqTopics;
import com.inno.leyin.common.vo.log.LeyinLogEvent;
import com.inno.leyin.im.util.RocketmqUtil;
import com.inno.leyin.im.util.SessionUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 控制层统一日志处理
 */
@Aspect
@Component
public class WebLogAspect extends AbstactWebLogAspect {

    @Autowired
    private RocketmqUtil rocketmqUtil;

    @Override
    public void sendLogEvent(LeyinLogEvent vo) {
        rocketmqUtil.sendMessage(MqTopics.SYS_LOG_EVENT,MqTopics.SYS_LOG_EVENT,vo);
    }
    
    //指定该切面要切入到哪些位置
    @Pointcut("execution(* com.inno.leyin.im.controller.*.*(..))")
    @Override
    public void weblogAspect(){}

    @Override
    public String getSessionUserId() {
        return SessionUtil.getUserId();
    }

    @Override
    @Before(value = "weblogAspect()")
    public void doBefore(JoinPoint joinPoint) {
        super.doBefore(joinPoint);

    }

}

加解密工具类,这个加密的方式可以各自取舍,没必要用我这种,我这种相对比较麻烦,采用的是RSA+AES的方式。友友们不一定需要照搬的

import cn.hutool.json.JSONUtil;
import com.inno.leyin.common.constant.CommonConstant;
import com.inno.leyin.common.dto.ResponseResult;
import com.inno.leyin.common.dto.SecurityQueryDto;
import com.inno.leyin.common.enums.ResponseMessage;
import com.inno.leyin.common.exception.ApiException;
import com.inno.leyin.common.util.AesEncryptUtil;
import com.inno.leyin.common.util.RandomUtil;
import io.swagger.models.auth.In;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Value;

import javax.crypto.Cipher;
import java.security.*;
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.Map;
import java.util.TreeMap;

/**
 * rsa 加密解密工具类
 */
public abstract class AbstactRSAUtil {

    @Value("${leyin.security.app.publicKey}")
    private String appPublicKey;
    @Value("${leyin.security.service.privateKey}")
    private String servicePrivateKey;
    // 验证安全时间
    @Value("${leyin.security.verificationTime}")
    private Long verificationTime;

    private static Map<Integer, String> keyMap = new HashMap<Integer, String>();  //用于封装随机产生的公钥与私钥

    /**
     * RSA公钥加密
     * @param str
     * @return
     */
    public String encryptByPublicKey(String str) throws Exception {
        return encryptByPublicKey(str, appPublicKey);
    }

    /**
     * 随机生成密钥对
     *
     * @throws NoSuchAlgorithmException
     */
    public static void genKeyPair() throws NoSuchAlgorithmException {
        // KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
        // 初始化密钥对生成器,密钥大小为96-1024位
        keyPairGen.initialize(1024, new SecureRandom());
        // 生成一个密钥对,保存在keyPair中
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();   // 得到私钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();  // 得到公钥
        String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
        // 得到私钥字符串
        String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded())));
        // 将公钥和私钥保存到Map
        keyMap.put(0, publicKeyString);  //0表示公钥
        keyMap.put(1, privateKeyString);  //1表示私钥
    }

    /**
     * RSA公钥加密
     *
     * @param str       加密字符串
     * @param publicKey 公钥
     * @return 密文
     * @throws Exception 加密过程中的异常信息
     */
    public static String encryptByPublicKey(String str, String publicKey) throws Exception {
        //base64编码的公钥
        byte[] decoded = Base64.decodeBase64(publicKey);
        RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
        //RSA加密
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
        String outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes("UTF-8")));
        return outStr;
    }


    /**
     * RSA私钥加密
     *
     * @param str           加密字符串
     * @param privateKeyStr 公钥
     * @return 密文
     * @throws Exception 加密过程中的异常信息
     */
    public static String encryptByPrivateKey(String str, String privateKeyStr) throws Exception {
        PrivateKey privateKey = getPrivateKey(privateKeyStr);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        byte[] cipherText = cipher.doFinal(str.getBytes());
        String cipherStr = new String(Base64.encodeBase64(cipherText));
        return cipherStr;
    }


    // 解密
    public String decryptByPrivateKey(String str) throws Exception {
        return decryptByPrivateKey(str, servicePrivateKey);
    }

    /**
     * RSA私钥解密
     *
     * @param str        加密字符串
     * @param privateKey 私钥
     * @return 铭文
     * @throws Exception 解密过程中的异常信息
     */
    public static String decryptByPrivateKey(String str, String privateKey) throws Exception {
        //64位解码加密后的字符串
        byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8"));
        //base64编码的私钥
        byte[] decoded = Base64.decodeBase64(privateKey);
        RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
        //RSA解密
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, priKey);
        String outStr = new String(cipher.doFinal(inputByte));
        return outStr;
    }

    /**
     * RSA公钥解密
     *
     * @param str          加密字符串
     * @param publicKeyStr 私钥
     * @return 铭文
     * @throws Exception 解密过程中的异常信息
     */
    public static String decryptByPublicKey(String str, String publicKeyStr) throws Exception {
        // 获取公钥
        PublicKey publicKey = getPublicKey(publicKeyStr);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        byte[] cipherText = Base64.decodeBase64(str);
        byte[] decryptText = cipher.doFinal(cipherText);
        return new String(decryptText);
    }

    // 将base64编码后的公钥字符串转成PublicKey实例
    private static PublicKey getPublicKey(String publicKey) throws Exception {
        byte[] keyBytes = Base64.decodeBase64(publicKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePublic(keySpec);
    }

    // 将base64编码后的私钥字符串转成PrivateKey实例
    private static PrivateKey getPrivateKey(String privateKey) throws Exception {
        byte[] keyBytes = Base64.decodeBase64(privateKey);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(keySpec);
    }

    // 解密
    public String decrypt(SecurityQueryDto model) throws Exception {
        String encryptStr = model.getData();
        if (model.getSecurity().equals(CommonConstant.SECURITY_TYPE_NONE)) {
            return model.getData();
        } else if (model.getSecurity().equals(CommonConstant.SECURITY_TYPE_RSA)) {
            return decryptByPrivateKey(encryptStr);
        } else if (model.getSecurity().equals(CommonConstant.SECURITY_TYPE_AES)) {
            // aes 解密
            String aesKey = decryptByPrivateKey(model.getAesKey());
            return AesEncryptUtil.decrypt(aesKey, model.getData());
        } else {
            throw new ApiException(ResponseMessage.SECURITY_ERROR);
        }
    }

    // 安全时间 一般5分钟
    public Long getVerificationTime() {
        return verificationTime * 1000;
    }

    // 加密实体 aes
    public SecurityQueryDto encryptAes(String jsonData) throws Exception {
        return encrypt(jsonData, CommonConstant.SECURITY_TYPE_AES);
    }

    // 加密实体 rsa
    public SecurityQueryDto encryptRsa(String jsonData) throws Exception {
        return encrypt(jsonData, CommonConstant.SECURITY_TYPE_RSA);
    }

    // 不加密
    public SecurityQueryDto encryptNone(String jsonData) throws Exception {
        return encrypt(jsonData, CommonConstant.SECURITY_TYPE_NONE);
    }

    // 加密
    public SecurityQueryDto encrypt(String jsonData, Integer securityType) throws Exception {
        SecurityQueryDto queryDto = new SecurityQueryDto();
        queryDto.setSecurity(securityType);
        if (securityType.equals(CommonConstant.SECURITY_TYPE_NONE)) {
            queryDto.setData(jsonData);
        } else if (securityType.equals(CommonConstant.SECURITY_TYPE_RSA)) {
            queryDto.setData(encryptByPublicKey(jsonData));
        } else if (securityType.equals(CommonConstant.SECURITY_TYPE_AES)) {
            String aesKey = RandomUtil.getRandomStr(16, RandomUtil.TYPE_LATTER);
            queryDto.setAesKey(encryptByPublicKey(aesKey));
            // aes 加密
            queryDto.setData(AesEncryptUtil.encrypt(aesKey, jsonData));
        } else {
            throw new ApiException(ResponseMessage.SECURITY_ERROR);
        }
        return queryDto;
    }

    // 加密
    public void encrypt(ResponseResult resp, Integer securityType) throws Exception {
        SecurityQueryDto queryDto = encrypt(JSONUtil.toJsonStr(resp.getData()),securityType);
        resp.setData(queryDto.getData());
        resp.setAesKey(queryDto.getAesKey());
        resp.setSecurity(securityType);
    }

    // 加密
    public void encrypt(SecurityQueryDto dto) throws Exception {
        SecurityQueryDto queryDto = encrypt(dto.getData(),dto.getSecurity());
        dto.setData(queryDto.getData());
        dto.setAesKey(queryDto.getAesKey());
    }

附上我的加解密思路

image.png

四、结尾

本篇api加解密方式的已经描述完毕。其实友友们需要理解我的实现思路(aop+注解+mq异步存请求日志),往后加密的细节部分因人而异,某些不重要的部分不用深究,如果想要照搬,可能没点基础也搬不过来,所以我还是强调理解我的思路后,再动手实践。


结语:以往都是看别人的博客进行学习技术,其中不乏有精华博客也有吊儿郎当的CV大法文章,所以决定将自己所学所用所整理的知识分享给大家,主要还是想为了后浪们少走些弯路,多些正能量的博客,如有错漏,欢迎指正,仅希望大家能在我的博客中学到知识,解决到问题,那么就足够了。谢谢大家!(转载请注明原文出处)