基于Springboot的AES报⽂解密
从前到后实现⼀个 springboot 使⽤Incepter解析AES密⽂。
1.什么是AES加密
AES是⼀种对称加密,这个标准⽤来替代原先的(Data Encryption Standard),已经被多⽅分析且⼴为全世界所使⽤。本⽂中AES加密⽅法同样适⽤于 DES。
AES使⽤起来⾮常简单,前后端需要⼀个相同的密钥,前端加密完后,将密⽂体发送到后端,后端使⽤将加密的报⽂拦截解析后再转发到相应处理的控制器controller。
那么如何知道哪些请求加密了哪些请求没加密呢?如何去有针对性的拦截加密的报⽂这也是我们关⼼的。
2.什么是
(Interceptor) 不依赖 Servlet 容器,依赖 Spring 等 Web 框架,在 SpringMVC 框架中是配置在SpringMVC 的配置⽂件中,在 SpringBoot 项⽬中也可以采⽤注解的形式实现。
它是链式调⽤,⼀个应⽤中可以同时存在多个Interceptor, ⼀个请求也可以触发多个 ,⽽每个的调⽤会依据它的声明顺序依次执⾏。
⾸先编写⼀个简单的处理类,请求的拦截是通过HandlerInterceptor 来实现,看到HandlerInterceptor 接⼝中也定义了三个⽅法。
preHandle() :这个⽅法将在请求处理之前进⾏调⽤。注意:如果该⽅法的返回值为false ,将视为当前请求结束,不仅⾃⾝的会失效,还会导致其他的也不再执⾏。
postHandle():只有在 preHandle() ⽅法返回值为true 时才会执⾏。会在Controller 中的⽅法调⽤之后,DispatcherServlet 返回渲染视图之前被调⽤。 有意思的是:postHandle() ⽅法被调⽤的顺序跟 preHandle() 是相反的,先声明的 preHandle() ⽅法先执⾏,⽽postHandle()⽅法反⽽会后执⾏。
afterCompletion():只有在 preHandle() ⽅法返回值为true 时才会执⾏。在整个请求结束之后, DispatcherServlet 渲染了对应的视图之后执⾏。
本⽂中处理加密解密从preHandle⽅法中执⾏
是 AOP 的⼀种应⽤,底层采⽤ Java 的反射机制来实现的。与过滤器(filter)⼀个很⼤的区别
是在中可以注⼊ Spring 的Bean,能够获取到各种需要的 Service 来处理业务逻辑,⽽过滤器则不⾏。
3.实现
3.1 前端:
1. 如果您是vue项⽬请您安装 CryptoJS
npm install crypto-js --save
2. js 的 AES⼯具类:
以下代码是 加密解密⼯具类。
其中key 是与后端约定好的 密钥(不能泄漏)
import CryptoJS from 'crypto-js'//引⽤AES源码js
const key = Utf8.parse("1234123412ABCDEF"); //⼗六位⼗六进制数作为秘钥
/**
* 加密,解密⼯具类
*/
function Encrypt(word){
var srcs = Utf8.parse(word);
var srcs = Utf8.parse(word);
var encrypted = pt(srcs, key, {
mode : de.ECB,
padding : CryptoJS.pad.Pkcs7
});
String() ;
// return String());
}
function Decrypt(word) {
//word = hexToStr(word);
var decrypt = CryptoJS.AES.decrypt(word, key, {
mode : de.ECB,
padding : CryptoJS.pad.Pkcs7
});
Utf8.stringify(decrypt).toString();
}
function strToHex(str) {
var val = "";
for (var i = 0; i < str.length; i++) {
if (val == "")
val = str.charCodeAt(i).toString(16);
else
val += str.charCodeAt(i).toString(16);
}
val += "0a";
return val;
}
function hexToStr(hex) {
debugger;
/
/hex = JSON.stringify(hex);
hex=hex+"";
var arr = hex.split("");
var out = ""
for (var i = 0; i < arr.length / 2; i++) {
var tmp = "0x" + arr[i * 2] + arr[i * 2 + 1]
var charValue = String.fromCharCode(tmp);
out += charValue;
}
return out;
}
function MD5(body){
return CryptoJS.MD5(body).toString();
}
export default {
Decrypt ,
Encrypt ,
MD5
}
3. 封装axios 请求。可以根据⾃⾝需求选择⼀些接⼝请求是否 加密或者不加密。
新建 http.js ,代码如下:
每当有axios请求时就会进⼊,判断config.data 是否有数据,config.data中是否存在 sdata 这个键,如果存在就取 sdata键值对应的值先转为字符串再使⽤ 加密⽅法加密。
当然 你也可以按照⾃⼰喜好去封装定义 sdata 只是我⾃⼰取名的。
当你是post请求 ,并且参数体严格按照 {sdata:{xxxx}} 格式,那xxxx部分将会加密。否则其他情况不会被axios 所拦截修改。
import axios from 'axios';
import secret from "./secret";
//拦截请求
quest.use(
config => {
//如果是post请求,即params中有数据且params包含 secretData 字段说明这是加密内容
if(config.data){
if(typeof(config.data.sdata) != 'undefined' || config.data.sdata != null){
config.data.sdata = secret.Encrypt(JSON.stringify(config.data.sdata)) ;
}
}
return config;
},
err => {
ject(err);
});
//拦截响应
sponse.use(
response => {
return response
},
error => {
sponse.data) // 返回接⼝返回的错误信息
})
export default axios ;
4.在js代码中使⽤ 封装好的axios 代码发送加密请求
以登录为例,以下调⽤⽅法为:
login({commit, dispatch, state}, payload) {
return new Promise((resolve, reject) => {
let loginVo = payload.loginVo;
axios.post('/api/tokens/login', {
sdata:{
username: loginVo.phone,
password: secret.MD5(loginVo.password)
}
})
.then(function (response) {
if (de == 0) {
var userInfo = response.data.data;
//登录成功后的逻辑
resolve(response);
} else {
reject("⽤户名或密码错误!");
}
})
.catch(function (error) {
console.log('login', error)
reject("登录出错!请重试");
});
});
}
3.2 后端
实现思路是这样的,当前端发起⼀个请求,⾸当其冲先拦截这个请求,对请求进⾏判断是否是需要解密的请求。 如果满⾜以下条件就对该请求进⾏解密:
1. post请求
2. content-type为 application/json
3. controller控制器必须有@SecurityParameter 注解修饰的才能被拦截
我们约定满⾜以上条件,就能拦截后解密,其他情况就直接放⾏,⽆需操作即可。
值的⼀提的是被@SecurityParameter 注解修饰的控制器就是表⽰此Controller 需要解密。前端加密的接⼝,对应的后台控制器必须有@SecurityParameter注解修饰,这就是约定。否则此注解将⽆意义。
1. AES加密⼯具类
其中的key 必须与前端 约定⼀致。
package com.shao.cursort.utils;
import com.alibaba.fastjson.JSONObject;
import dec.binary.Base64;
pto.Cipher;
pto.KeyGenerator;
pto.spec.IvParameterSpec;
pto.spec.SecretKeySpec;
import java.util.HashMap;
import java.util.Map;
/**
* 前后端数据传输加密⼯具类
* @author shaoduo
* @author shaoduo
*
*/
public class AesEncryptUtils {
//可配置到Constant中,并读取配置⽂件注⼊,16位,⾃⼰定义
private static final String KEY = "1234123412ABCDEF";
//参数分别代表算法名称/加密模式/数据填充⽅式
private static final String ALGORITHMSTR = "AES/ECB/PKCS5Padding";
/**
* 加密
* @param content 加密的字符串
* @param encryptKey key值
* @return
* @throws Exception
*/
public static String encrypt(String content, String encryptKey) throws Exception {
KeyGenerator kgen = Instance("AES");springboot aop
kgen.init(128);
Cipher cipher = Instance(ALGORITHMSTR);
cipher.init(Cipher.ENCRYPT_MODE, new Bytes(), "AES"));
byte[] b = cipher.Bytes("utf-8"));
// 采⽤base64算法进⾏转码,避免出现中⽂乱码
deBase64String(b);
}
/**
* 解密
* @param encryptStr 解密的字符串
* @param decryptKey 解密的key值
* @return
* @throws Exception
*/
public static String decrypt(String encryptStr, String decryptKey) throws Exception {
KeyGenerator kgen = Instance("AES");
kgen.init(128);
Cipher cipher = Instance(ALGORITHMSTR);
cipher.init(Cipher.DECRYPT_MODE, new Bytes("utf-8"), "AES")); // 采⽤base64算法进⾏转码,避免出现中⽂乱码
//byte[] b = hex2Bytes(encryptStr) ;
byte[] encryptBytes = Base64.decodeBase64(encryptStr);
byte[] decryptBytes = cipher.doFinal(encryptBytes);
return new String(decryptBytes);
}
public static String encrypt(String content) throws Exception {
return encrypt(content, KEY);
}
public static String decrypt(String encryptStr) throws Exception {
return decrypt(encryptStr, KEY);
}
/
* public static void main(String[] args) throws Exception {
Map map=new HashMap<String,String>();
map.put("key","value");
map.put("中⽂","汉字");
String content = JSONString(map);
System.out.println("加密前:" + content);
String encrypt = encrypt(content, KEY);
System.out.println("加密后:" + encrypt);
String decrypt = decrypt(encrypt, KEY);
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论