前言

特别是在做一下政府项目时,有时候需要对所有接口数据进行加密。

加密传输结构体定义

  • 请求头
localhost:8080/user/info
header:
Content-Type:application/json
signature:55efb04a83ca083dd1e6003cde127c45
timestamp:1648308048
salt:123456
clientType:ANDORID
  • body体
// 原始请求体
{"page": 1,"size": 10
}
// 加密后的请求体
{"data": "1ZBecdnDuMocxAiW9UtBrJzlvVbueP9K0MsIxQccmU3OPG92oRinVm0GxBwdlXXJ"
}// 加密响应体:
{"data": "fxHYvnIE54eAXDbErdrDryEsIYNvsOOkyEKYB1iBcre/QU1wMowHE2BNX/je6OP3NlsCtAeDqcp7J1N332el8q2FokixLvdxAPyW5Un9JiT0LQ3MB8p+nN23pTSIvh9VS92lCA8KULWg2nViSFL5X1VwKrF0K/dcVVZnpw5h227UywP6ezSHjHdA+Q0eKZFGTEv3IzNXWqq/otx5fl1gKQ==","code": 200,"signature": "aa61f19da0eb5d99f13c145a40a7746b","msg": "","timestamp": 1648480034,"salt": 632648
}
// 解密后的响应体:
{"code": 200,"data": [{"id": 1,"name": "boyka","registerTime": "2022-03-27T00:19:43.699","userType": "COMMON"}],"msg": "用户列表查询成功","salt": 0
}

AES加密工具类

import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.util.StringUtils;
import sun.misc.BASE64Decoder;import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;/***/
public class EncryptUtils {private EncryptUtils() {}private static final String ALGORITHM = "AES/CBC/PKCS5Padding";public static final String IV = "2233445566778899";/*** base 64 encode** @param bytes 待编码的byte[]* @return 编码后的base 64 code*/public static String base64Encode(byte[] bytes) {String data = Base64.encodeBase64String(bytes);if (!StringUtils.isEmpty(data)) {// 处理一行超过76个字符换行问题return data.replaceAll("[\\s*\t\n\r]", "");} else {return data;}}/*** base 64 decode** @param base64Code 待解码的base 64 code* @return 解码后的byte[]* @throws Exception*/public static byte[] base64Decode(String base64Code) throws Exception {return StringUtils.isEmpty(base64Code) ? null : new BASE64Decoder().decodeBuffer(base64Code);}/*** AES加密** @param content    待加密的内容* @param encryptKey 加密密钥* @return 加密后的byte[]* @throws Exception*/public static byte[] aesEncryptToBytes(String content, String encryptKey) throws Exception {byte[] raw = encryptKey.getBytes();SecretKeySpec secretKeySpec = new SecretKeySpec(raw, "AES");Cipher cipher = Cipher.getInstance(ALGORITHM);//"算法/模式/补码方式"IvParameterSpec iv = new IvParameterSpec(IV.getBytes());//使用CBC模式,需要一个向量iv,可增加加密算法的强度cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, iv);return cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));}/*** AES解密** @param encryptBytes 待解密的byte[]* @param decryptKey   解密密钥* @return 解密后的String* @throws Exception*/public static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) throws Exception {byte[] raw = decryptKey.getBytes();SecretKeySpec secretKeySpec = new SecretKeySpec(raw, "AES");Cipher cipher = Cipher.getInstance(ALGORITHM);//"算法/模式/补码方式"IvParameterSpec iv = new IvParameterSpec(IV.getBytes());//使用CBC模式,需要一个向量iv,可增加加密算法的强度cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, iv);byte[] decryptBytes = cipher.doFinal(encryptBytes);return new String(decryptBytes);}/*** AES加密为base 64 code** @param content    待加密的内容* @param encryptKey 加密密钥* @return 加密后的base 64 code* @throws Exception*/public static String aesEncrypt(String content, String encryptKey) throws Exception {return base64Encode(aesEncryptToBytes(content, encryptKey));}/*** 将base 64 code AES解密** @param encryptStr 待解密的base 64 code* @param decryptKey 解密密钥* @return 解密后的string* @throws Exception*/public static String aesDecrypt(String encryptStr, String decryptKey) throws Exception {return StringUtils.isEmpty(encryptStr) ? null : aesDecryptByBytes(base64Decode(encryptStr), decryptKey);}public static void main(String[] args) throws Exception {String timestamp = "1648308048";String salt = "123456";String data = "{\n" +"\t\"page\": 1,\n" +"\t\"size\": 10\n" +"}";String privateKey = "8VBkO5amPcPrXV3m";String encryptData = aesEncrypt(data, privateKey);System.out.println(encryptData);encryptData = "fxHYvnIE54eAXDbErdrDryEsIYNvsOOkyEKYB1iBcre/QU1wMowHE2BNX/je6OP3NlsCtAeDqcp7J1N332el8r4JPvZmKlXKUUReD8ayUckrFQkSATLwASJmMTArIeit28+LbLFz9e9K0vdaRi/splThh1+7u5CDg/ixDWwVThOlZknRgP3RKar6KN7BJGFyCJYNqpYPkuapsa2B2B6DHQ==";String decryptData = aesDecrypt(encryptData, privateKey);System.out.println(decryptData);String signature = Md5Utils.genSignature(timestamp + salt + encryptData + privateKey);System.out.println(signature);}public static byte[] generateDesKey(int length) throws Exception {//实例化KeyGenerator kgen = KeyGenerator.getInstance("AES");//设置密钥长度kgen.init(length);//生成密钥SecretKey skey = kgen.generateKey();//返回密钥的二进制编码return skey.getEncoded();}public static int genSalt() {int salt = 0;while (true) {salt = (int) (Math.random() * 1000000);if (salt < 1000000 && salt > 99999)break;}return salt;}
}

Md5Utils.java


import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;public class Md5Utils {/*** 生成签名** @param value 数据* @return 结果*/public static String genSignature(String value) {try {// 得到一个信息摘要器MessageDigest digest = MessageDigest.getInstance("md5");byte[] result = digest.digest(value.getBytes());StringBuilder buffer = new StringBuilder();// 把每个byte 做一个与运算 0xff;for (byte b : result) {// 与运算int number = b & 0xff;// 加盐String str = Integer.toHexString(number);if (str.length() == 1) {buffer.append("0");}buffer.append(str);}// 标准的md5加密后的结果return buffer.toString();} catch (NoSuchAlgorithmException e) {return "";}}
}

加密拦截器与过滤

SecretFilter

import org.springframework.beans.factory.annotation.Value;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;/*** @description: 过滤器*/
public class SecretFilter implements Filter {public static ThreadLocal<Boolean> secretThreadLocal = new ThreadLocal<>();@Value("${secret.privateKey.h5}")private String h5Key;@Value("${secret.privateKey.default}")private String defaultKey;public static ThreadLocal<String> clientPrivateKeyThreadLocal = new ThreadLocal<>();@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse response = (HttpServletResponse) servletResponse;// 免加密if (!request.getRequestURI().startsWith(SecretConstant.PREFIX)) {secretThreadLocal.set(Boolean.FALSE);filterChain.doFilter(request, response);} else {// 加密,先只考虑POST情况secretThreadLocal.set(Boolean.TRUE);// 简单获取对应加密的私钥String privateKey = ("H5".equalsIgnoreCase(Objects.requireNonNull(request.getHeader("clientType")))) ? h5Key : defaultKey;clientPrivateKeyThreadLocal.set(privateKey);filterChain.doFilter(request, response);}}
}
  • 请求参数处理类
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter;import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.Objects;import static cn.boykaff.encrypt.common.base.ResponseCode.SECRET_API_ERROR;@ControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
@Slf4j
public class SecretRequestAdvice extends RequestBodyAdviceAdapter {@Overridepublic boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {return true;}@Overridepublic HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {//如果支持加密消息,进行消息解密。String httpBody;if (Boolean.TRUE.equals(SecretFilter.secretThreadLocal.get())) {httpBody = decryptBody(inputMessage);} else {httpBody = StreamUtils.copyToString(inputMessage.getBody(), Charset.defaultCharset());}//返回处理后的消息体给messageConvertreturn new SecretHttpMessage(new ByteArrayInputStream(httpBody.getBytes()), inputMessage.getHeaders());}/*** 解密消息体** @param inputMessage 消息体* @return 明文*/private String decryptBody(HttpInputMessage inputMessage) throws IOException {InputStream encryptStream = inputMessage.getBody();String requestBody = StreamUtils.copyToString(encryptStream, Charset.defaultCharset());// 验签过程HttpHeaders headers = inputMessage.getHeaders();if (CollectionUtils.isEmpty(headers.get("clientType"))|| CollectionUtils.isEmpty(headers.get("timestamp"))|| CollectionUtils.isEmpty(headers.get("salt"))|| CollectionUtils.isEmpty(headers.get("signature"))) {throw new ResultException(SECRET_API_ERROR, "请求解密参数错误,clientType、timestamp、salt、signature等参数传递是否正确传递");}String timestamp = String.valueOf(Objects.requireNonNull(headers.get("timestamp")).get(0));String salt = String.valueOf(Objects.requireNonNull(headers.get("salt")).get(0));String signature = String.valueOf(Objects.requireNonNull(headers.get("signature")).get(0));String privateKey = SecretFilter.clientPrivateKeyThreadLocal.get();ReqSecret reqSecret = JSON.parseObject(requestBody, ReqSecret.class);String data = reqSecret.getData();String newSignature = "";if (!StringUtils.isEmpty(privateKey)) {newSignature = Md5Utils.genSignature(timestamp + salt + data + privateKey);}if (!newSignature.equals(signature)) {// 验签失败throw new ResultException(SECRET_API_ERROR, "验签失败,请确认加密方式是否正确");}try {String decrypt = EncryptUtils.aesDecrypt(data, privateKey);if (StringUtils.isEmpty(decrypt)) {decrypt = "{}";}return decrypt;} catch (Exception e) {log.error("error: ", e);}throw new ResultException(SECRET_API_ERROR, "解密失败");}
}
  • 相应参数处理类
    SecretResponseAdvice
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;import static cn.boykaff.encrypt.common.base.ResponseCode.SECRET_API_ERROR;/*** @description:* @author: boykaff* @date: 2022-03-25-0025*/
@ControllerAdvice
public class SecretResponseAdvice implements ResponseBodyAdvice {private Logger logger = LoggerFactory.getLogger(SecretResponseAdvice.class);@Autowiredprivate ObjectMapper objectMapper;@Overridepublic boolean supports(MethodParameter methodParameter, Class aClass) {return true;}@Overridepublic Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {// 判断是否需要加密Boolean respSecret = SecretFilter.secretThreadLocal.get();String secretKey = SecretFilter.clientPrivateKeyThreadLocal.get();// 清理本地缓存SecretFilter.secretThreadLocal.remove();SecretFilter.clientPrivateKeyThreadLocal.remove();if (null != respSecret && respSecret) {if (o instanceof ResponseBasic) {// 外层加密级异常if (SECRET_API_ERROR == ((ResponseBasic) o).getCode()) {return SecretResponseBasic.fail(((ResponseBasic) o).getCode(), ((ResponseBasic) o).getData(), ((ResponseBasic) o).getMsg());}// 业务逻辑try {// 使用FastJson序列号会导致和之前的接口响应参数不一致,后面会重点讲到String data = EncryptUtils.aesEncrypt(objectMapper.writeValueAsString(o), secretKey);// 增加签名long timestamp = System.currentTimeMillis() / 1000;int salt = EncryptUtils.genSalt();String dataNew = timestamp + "" + salt + "" + data + secretKey;String newSignature = Md5Utils.genSignature(dataNew);return SecretResponseBasic.success(data, timestamp, salt, newSignature);} catch (Exception e) {logger.error("beforeBodyWrite error:", e);return SecretResponseBasic.fail(SECRET_API_ERROR, "", "服务端处理结果数据异常");}}}return o;}
}

过滤器配置

@Configuration
public class WebConfig {@Beanpublic Filter secretFilter() {return new SecretFilter();}@Beanpublic FilterRegistrationBean filterRegistration() {FilterRegistrationBean registration = new FilterRegistrationBean();registration.setFilter(new DelegatingFilterProxy("secretFilter"));registration.setName("secretFilter");registration.addUrlPatterns(PREFIX + "/*");registration.setOrder(1);return registration;}
}

总结

好了到这里大的骨架就已经有了,这样就完成了加解密的过程。
客户端请求加密->发起请求->服务端解密->业务处理->服务端响应加密->客户端解密展示

怎样来实现数据接口的加密?相关推荐

  1. 爬虫入门2 数据接口加密以及爬虫案例

    爬虫加密 爬虫加密主要分为 HTML 实体编码加密.Unicode 加密方式.数据接口加密. 1 HTML 实体编码 1.1 实例地址 猫眼地址:http://pf.st.maoyan.com/das ...

  2. 如何优雅的实现 Spring Boot 接口参数加密解密?

    点击关注公众号,Java干货及时送达 加密解密本身并不是难事,问题是在何时去处理?定义一个过滤器,将请求和响应分别拦截下来进行处理也是一个办法,这种方式虽然粗暴,但是灵活,因为可以拿到一手的请求参数和 ...

  3. 数据接口的登录态校验以及JWT

    混合开发的时候是怎么做的 前后端混合开发的时候,用户登录状态的管理一般都是通过session来实现的,原理很简单:用户登录后,服务端将登录用户信息存储到服务器上的特定位置,并生成对应的session ...

  4. 探讨NET Core数据进行3DES加密或解密弱密钥问题

    [导读]之前写过一篇<探讨.NET Core数据进行3DES加密和解密问题>,最近看到有人提出弱密钥问题,换个强密钥不就完了吗,猜测可能是与第三方对接导致很无奈不能更换密钥,所以产生本文解 ...

  5. java 接口 安全加密_Java中的安全加密

    java 接口 安全加密 上一次我写关于密码学的文章时 ,我概述了Apache Shiro加密API,并展示了如何使用其两个对称密码. 我还写道:"您不需要在应用程序中加密和解密敏感数据的更 ...

  6. 【JS 逆向百例】Ether Rock 空投接口 AES256 加密分析

    关注微信公众号:K哥爬虫,持续分享爬虫进阶.JS/安卓逆向等技术干货! 声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后 ...

  7. 数据脱敏和加密_Apache ShardingSphere数据脱敏全解决方案详解

    解决方案详解 在了解了ShardingSphere脱敏处理流程后,即可将脱敏配置.脱敏处理流程与实际场景进行结合.所有的设计开发都是为了解决业务场景遇到的痛点.那么面对之前提到的业务场景需求,又应该如 ...

  8. 接口访问加密_加密“访问”的争论日益激烈

    接口访问加密 即使美国政府再次竞购Clipper Chip ,这是一个臭名昭著的事件,多年来一直影响着密码学的辩论,但在许多季度中,人们重新开始关注不应重复的情况. 最新证据来自联合国人权事务高级专员 ...

  9. SpringBoot 实现接口参数加密解密功能

    加密解密本身并不是难事,问题是在何时去处理?定义一个过滤器,将请求和响应分别拦截下来进行处理也是一个办法,这种方式虽然粗暴,但是灵活,因为可以拿到一手的请求参数和响应数据.不过 SpringMVC 中 ...

最新文章

  1. python的优缺点-Python 有什么一般人不知道的缺点?
  2. js切换换class
  3. ArcGIS Server9.2学习开发(4)——使用Toc控件
  4. python tkinter详解 博客园_python tkinter-布局
  5. codevs原创抄袭题 5960 信使
  6. java 最好 入门_C++和Java哪个比较好入门?初学者该如何选择?
  7. @configurationproperties注解的使用_SpringBoot常用注解的简单理解
  8. STM32 LED灯的另一种写法
  9. js执行环境作用域和闭包_JavaScript中执行上下文,提升,作用域和闭包的终极指南
  10. 浅谈BP神经网络的Matlab实现
  11. mysql memory优点_MySQL Memory存储引擎:优势及性能测试
  12. Android API中文文档(111) —— MailTo
  13. c语言单循环赛制,循环赛日程安排问题(分治法)
  14. c语言表达式判断语法错误题,C语言数据类型与表达式习题及答案.doc
  15. 单片机中常用的串口通信协议帧
  16. 基于51单片机的脉搏测量仪protues仿真设计
  17. 长江中下游先民最早驯化野生稻 国稻种芯:全球35亿人口主食
  18. 作为程序员,我到底在恐慌什么!
  19. 谓词下推原理和数据框架的应用
  20. 信丰二中2021高考成绩查询,信丰二中

热门文章

  1. python中time是什么意思_python中time的基本介绍
  2. 如何专业查看视频/音频/图片信息
  3. Python定时任务推送微信消息
  4. Matplotlib绘图库入门(一):pyplot绘图基础
  5. Android中全局搜索(QuickSearchBox)详解(一)
  6. 目标检测+图像分割项目
  7. 数据库系统概论练习3
  8. Spark:reduceByKey与groupByKey进行对比
  9. eclipse上插入中文到mysql,但是navicat显示问号《网上很多方法都没用》,最终google到了精品
  10. Android OKHttp 可能你从来没用过的拦截器 【实用推荐】