教你如何使用AES对接口参数进行加密

教你如何使用AES对接口参数进行加密

前言

我们作为程序猿,在浏览网站的时候偶尔也会打开控制台看看请求的接口,我们会发现有些接口的传输是 “乱码” ,那么这个乱码究竟是什么呢?为什么要这么做?

其实这个所谓的 “乱码” 其实是一种加密后的密文,其原理是前后端提前约定好一种协议,在该协议下进行加解密的处理,例如:前端将数据加密后发送给后端,后端接收到参数后,第一时间先在约定好的协议下将密文解密成可识别的对象,再进行逻辑处理,最后将结果加密返回给前端,前端获取到密文后,同样依照约定好的协议对密文进行解密,最后将解密出来的数据拿来使用。

那么我们想实现同样的效果,应该如何做呢?别急,听哥们给你一一道来。

介绍

一般来说加密算法会分为两种:对称加密算法非对称加密算法

对称加密算法

摘自百度百科: 采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。

非对称加密算法

摘自百度百科: 不对称加密算法使用两把完全不同但又是完全匹配的一对钥匙—公钥和私钥。在使用不对称加密算法加密文件时,只有使用匹配的一对公钥和私钥,才能完成对明文的加密和解密过程。

经过百度百科中的简单概要,我们已经知道了对称加密算法非对称加密算法 都是什么,但它们中间又有什么不同呢?早就猜到你会这么问了,所以我已经把它们的区别一一列出来了。

区别

密钥

对称加密: 一共只有一种密钥,并将该密钥同时用来加解密。

非对称加密: 共有两种密钥:公钥私钥 ,使用公钥来加密,使用私钥来解密。

速度

对称加密: 算法简单且加解密容易,所以执行效率高、速度快。

非对称加密: 由于加密算法比较复杂,所以加解密的效率很低,速度远不如 对称加密

安全性

对称加密: 由于加解密均使用的为同一个密钥,那么若密钥泄露则有被破解密文的风险。

非对称加密: 由于使用了两种密钥,且公钥是可公开的密钥,使用私钥来进行解密,消除了用户交换密钥的条件,极大程度上保证了数据安全。

实现

在这里给大家介绍一下 AES + CBC + PKCS5Padding 的加密方式,具体实现如下:

引入依赖

<dependency><groupId>org.apache.directory.studio</groupId><artifactId>org.apache.commons.codec</artifactId><version>1.8</version></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.13.0</version></dependency>

编写密钥荷载

注意:这里的 AES_KEYAES_IV 可以自定义,但 必须是16位的

/*** @author Bummon* @description 荷载* @date 2023-08-12 10:27*/
public class Common {/*** AES密钥*/public static final byte[] AES_KEY = "Ct9x5IUNHlhq0siZ".getBytes();/*** AES偏移*/public static final byte[] AES_IV = "MIIBIjANBgkqhkiG".getBytes();}

编写AES工具类

import com.test.constant.Common;
import org.apache.commons.codec.binary.Base64;import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;/*** @author Bummon* @description AES工具类* @date 2023-08-12 09:26*/
public class AESUtils {private static final String ALGORITHMSTR = "AES/CBC/PKCS5Padding";/*** @param content 加密内容* @return {@link String}* @date 2023-08-12 09:27* @author Bummon* @description 加密*/public static String encrypt(String content) {String encode = null;try {Cipher cipher = initCipher(Cipher.ENCRYPT_MODE);byte[] encodeBytes = cipher.doFinal(content.getBytes());encode = Base64.encodeBase64String(encodeBytes);} catch (Exception e) {e.printStackTrace();}return encode;}public static String decrypt(String encryptStr) {String decode = null;try {Cipher cipher = initCipher(Cipher.DECRYPT_MODE);byte[] encodeBytes = Base64.decodeBase64(encryptStr);byte[] decodeBytes = cipher.doFinal(encodeBytes);decode = new String(decodeBytes);} catch (Exception e) {e.printStackTrace();}return decode;}/*** @param cipherMode 操作类型 加密/解密* @return {@link Cipher}* @date 2023-08-12 09:42* @author Bummon* @description 初始化Cipher*/private static Cipher initCipher(int cipherMode) throws Exception {KeyGenerator kgen = KeyGenerator.getInstance("AES");kgen.init(128);Cipher cipher = Cipher.getInstance(ALGORITHMSTR);SecretKeySpec keySpec = new SecretKeySpec(Common.AES_KEY, "AES");IvParameterSpec ivParam = new IvParameterSpec(Common.AES_IV);cipher.init(cipherMode, keySpec, ivParam);return cipher;}public static void main(String[] args) {String encrypt = AESUtils.encrypt("Hello World");String decrypt = AESUtils.decrypt(encrypt);System.out.println(encrypt);System.out.println(decrypt);}
}

自定义注解

该注解作用于接口上,可以对接口的加密或者解密实现更加粒子化的控制,默认入参解密,出参加密。

import org.springframework.web.bind.annotation.Mapping;
import java.lang.annotation.*;/*** @author Bummon* @description AES加解密注解* @date 2023-08-12 09:44*/
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Mapping
@Documented
public @interface AES {/*** 入参是否解密,默认解密*/boolean inDecode() default true;/*** 出参是否加密,默认加密*/boolean outEncode() default true;
}

DecodeRequestBodyAdvice

import com.test.anno.AES;
import com.test.util.pwd.AESUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;/*** @author Bummon* @date 2023-08-12 10:22* @description 请求数据解密*/
@Slf4j
@ControllerAdvice(basePackages = "com.test.controller")
public class DecodeRequestBodyAdvice implements RequestBodyAdvice {@Overridepublic boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {return true;}@Overridepublic Object handleEmptyBody(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {return body;}@Overridepublic HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {try {boolean encode = false;if (methodParameter.getMethod().isAnnotationPresent(AES.class)) {//获取注解配置的包含和去除字段AES aes = methodParameter.getMethodAnnotation(AES.class);//入参是否需要解密encode = aes.decode();}if (encode) {log.info("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行解密");return new MyHttpInputMessage(inputMessage);} else {return inputMessage;}} catch (Exception e) {e.printStackTrace();log.error("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行解密出现异常:" + e.getMessage());return inputMessage;}}@Overridepublic Object afterBodyRead(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {return body;}class MyHttpInputMessage implements HttpInputMessage {private HttpHeaders headers;private InputStream body;public MyHttpInputMessage(HttpInputMessage inputMessage) throws Exception {this.headers = inputMessage.getHeaders();String param = IOUtils.toString(inputMessage.getBody(), "UTF-8");//去除请求数据中的转义字符String encryptStr = easpString(param).replace("\"", "");String decrypt = AESUtils.decrypt(encryptStr);this.body = IOUtils.toInputStream(decrypt, "UTF-8");}@Overridepublic InputStream getBody() throws IOException {return body;}@Overridepublic HttpHeaders getHeaders() {return headers;}/*** @param param* @return*/public String easpString(String param) {if (param != null && !param.equals("")) {String s = "{\"param\":";//去除param中的转义字符String data = param.replaceAll("\\s*|\r|\n|\t", "");if (!data.startsWith(s)) {throw new RuntimeException("参数【param】缺失异常!");} else {int closeLen = data.length() - 1;int openLen = "{\"param\":".length();String substring = StringUtils.substring(data, openLen, closeLen);return substring;}}return "";}}
}

EncodeResponseBodyAdvice

import com.fasterxml.jackson.databind.ObjectMapper;
import com.test.anno.AES;
import com.test.util.pwd.AESUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
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.ResponseBodyAdvice;/*** @author Bummon* @date 2023-08-12 10:36* @description 返回参数加密*/
@Slf4j
@ControllerAdvice(basePackages = "com.test.controller")
public class EncodeResponseBodyAdvice implements ResponseBodyAdvice {@Overridepublic boolean supports(MethodParameter methodParameter, Class aClass) {return true;}@Overridepublic Object beforeBodyWrite(Object body,MethodParameter methodParameter,MediaType mediaType,Class aClass,ServerHttpRequest serverHttpRequest,ServerHttpResponse serverHttpResponse) {boolean encode = false;if (methodParameter.getMethod().isAnnotationPresent(AES.class)) {//获取注解配置的包含和去除字段AES aes = methodParameter.getMethodAnnotation(AES.class);//出参是否需要加密encode = aes.encode();}if (encode) {log.info("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行加密");ObjectMapper objectMapper = new ObjectMapper();try {String result = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(body);return AESUtils.encrypt(result);} catch (Exception e) {e.printStackTrace();log.error("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行解密出现异常:" + e.getMessage());}}return body;}
}

编写测试控制器

import java.util.HashMap;
import java.util.Map;/*** @author Bummon* @description* @date 2023-08-12 10:37*/
@RestController
public class TestController {@AES(decode = false)@GetMapping("/getSecret")public Object getSecret() {Map<String, Object> map = new HashMap<>();map.put("name", "Bummon");map.put("homeUrl", "https://www.bummon.com/");map.put("blogUrl", "https://blog.bummon.com/");return map;}@AES(encode = false)@PostMapping("/getBySecret")public Object getBySecret(@RequestBody Map<String, Object> map) {return map;}
}

我们在这里编写了两个接口,其中 getSecret 接口不对入参进行解密,对出参进行加密,也就是前端传明文,后端返回为密文。getBySecret 接口是对入参进行解密,不对出参加密,也就是前端传密文,后端返回为明文。

我们的测试思路就是先测试getSecret 接口,同时也获取到了密文,在测试getBySecret 接口时将getSecret 接口返回的密文作为参数传进去。

测试

image.png

我们通过getSecret 接口拿到了密文,接下来将该密文作为参数调用getBySecret 接口。

image.png

可以看到我们成功将密文解析了出来,并且对接口入参没有影响。

感谢观看。


推荐

关注博客和公众号获取最新文章

Bummon’s Blog | Bummon’s Home | 公众号

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/63596.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度学习常用的python库学习笔记

文章目录 数据分析四剑客Numpyndarray数组和标量之间的运算基本的索引和切片数学和统计方法线性代数 PandasMatplotlibPIL 数据分析四剑客 Numpy Numpy中文网 ndarray 数组和标量之间的运算 基本的索引和切片 数学和统计方法 线性代数 Pandas Pandas中文网 Matplotlib Mat…

3.2 Tomcat基础

1. Tomcat概述 Tomcat 服务器是一个免费的开放源代码的Web 应用服务器&#xff0c;属于轻量级应用服务器。 Tomcat版本&#xff1a;apache-tomcat-8.5.76。 2.IDEA集成Tomcat 第一步 第二步 第三步 ​ 编辑切换为居中 添加图片注释&#xff0c;不超过 140 字&#xff0…

【Kubernetes】神乎其技的K8s到底是什么,为什么被越来越多人使用

&#x1f680;欢迎来到本文&#x1f680; &#x1f349;个人简介&#xff1a;陈童学哦&#xff0c;目前学习C/C、算法、Python、Java等方向&#xff0c;一个正在慢慢前行的普通人。 &#x1f3c0;系列专栏&#xff1a;陈童学的日记 &#x1f4a1;其他专栏&#xff1a;CSTL&…

最容易理解的C51单片机4位密码锁示例代码(附proteus电路图)

说明&#xff1a;开机启动就是上图这样的&#xff0c;密码正确显示P&#xff08;pass&#xff09;,密码错误显示E&#xff08;error&#xff09; #include "reg51.h" #include "myheader.h" #define uchar unsigned char long int sleep_i0; int pwd[4]{0…

用HTML+JavaScript构建C++类(Class)代码转换为MASM32代码的平台

一、需求分析 在使用MASM32编写Windows应用程序时&#xff0c;经常要调用Windows API接口函数 和 相应的数据结构&#xff0c;这些数据结构中有很多是类&#xff08;Class&#xff09;&#xff0c;对于那些在MASM32没有定义的类&#xff0c;我们需要自己来转换。比如&#xff…

编写一个指令(v-focus2end)使输入框文本在聚焦时焦点在文本最后一个位置

项目反馈输入框内容比较多时候&#xff0c;让鼠标光标在最后一个位置&#xff0c;心想什么奇葩需求&#xff0c;后面试了一下&#xff0c;是有点影响体验&#xff0c;于是就有了下面的效果&#xff0c;我目前的项目都是若依的架子&#xff0c;用的是vue2版本。vue3的朋友想要使…

OneFlow 中的 Softmax

Softmax 是深度学习模型中的常见算子。PyTorch 的 Softmax 算子直接调用 cuDNN 的接口。而 OneFlow 内部针对输入数据的类别数量&#xff0c;采用3个 kernel 来分别处理&#xff0c;在多数情况下都可以获得比 cuDNN 更优的性能表现。测试结果可见 如何实现一个高效的Softmax CU…

Kubernetes Calico

Calico以其性能、灵活性和网络策略而闻名&#xff0c;不仅涉及在主机和Pod之间提供网络连接&#xff0c;而且还涉及网络安全性和策略管理。(还可以配置防火墙规则来隔离不同应用的网络) 对于同网段通信&#xff0c;基于第3层&#xff0c;Calico使用BGP路由协议在主机之间路由数…

uni-app:实现点击按钮,进行数据累加展示(解决数据过多,导致出错)

效果 代码 核心代码 一、标签显示 <!-- 加载更多 --> <view class"load_more" v-if"info.length > pageNum * pageSize" tap"loadMore">加载更多 </view> v-if"info.length > pageNum * pageSize"&#xf…

网站SSL安全证书是什么及其重要性

网站SSL安全证书具体来说是一个数字文件&#xff0c;是由受信任的数字证书颁发机构&#xff08;CA机构&#xff09;进行审核颁发的&#xff0c;其中包含CA发布的信息&#xff0c;该信息表明该网站已使用加密连接进行了安全保护。 网站SSL安全证书也被称为SSL证书、https证书和…

多区域平台lazada,虾皮电商商品详情API接口返回值说明

Lazada和虾皮&#xff08;Shopee&#xff09;都是知名的电商平台&#xff0c;主要在东南亚地区运营。以下是关于它们的一些信息&#xff1a; Lazada&#xff08;来赞达&#xff09;&#xff1a; Lazada成立于2012年&#xff0c;起初是一个全球性的电子商务平台&#xff0c;后来…