vue-springboot前后端分离使用AES加解密,笔记
前端:1.安装crypto-js通过npm installcrypto-js -s2.封装cryptoJs,自定义加密和解密方法方式,导出方法import CryptoJS from 'crypto-js/crypto-js'// 默认的 KEY 与 iv 与后端保持一致 ,不采用后端传值密钥const KEY = CryptoJS.enc.Utf8.pars...
·
前端:
1.安装 crypto-js 通过npm install crypto-js -s
2.封装cryptoJs,自定义加密和解密方法方式,导出方法
import CryptoJS from 'crypto-js/crypto-js'
// 默认的 KEY 与 iv 与后端保持一致 ,不采用后端传值密钥
const KEY = CryptoJS.enc.Utf8.parse('aaDJL2d9DfhLZO0z');// 密钥
const IV = CryptoJS.enc.Utf8.parse('412ADDSSFA342442');// 偏移量
/**
* AES加密 :字符串 key iv 返回base64
*/
export function Encrypt(word, keyStr, ivStr) {
let key = KEY;
let iv = IV;
if (keyStr) {
key = CryptoJS.enc.Utf8.parse(keyStr);
iv = CryptoJS.enc.Utf8.parse(ivStr);
}
let srcs = CryptoJS.enc.Utf8.parse(word);
var encrypted = CryptoJS.AES.encrypt(srcs, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.ZeroPadding
});
return CryptoJS.enc.Base64.stringify(encrypted.ciphertext);
}
/**
* AES 解密 :字符串 key iv 返回base64
*
*/
export function Decrypt(word, keyStr, ivStr) {
let key = KEY;
let iv = IV;
if (keyStr) {
key = CryptoJS.enc.Utf8.parse(keyStr);
iv = CryptoJS.enc.Utf8.parse(ivStr);
}
let base64 = CryptoJS.enc.Base64.parse(word);
let src = CryptoJS.enc.Base64.stringify(base64);
var decrypt = CryptoJS.AES.decrypt(src, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.ZeroPadding
});
var decryptedStr = decrypt.toString(CryptoJS.enc.Utf8);
return decryptedStr.toString();
}
3.安装axios npm install axios --s
4.封装axios,统一处理请求和响应
/**
* axios请求配置
*/
import Vue from 'vue'
import http from 'axios'
import {getToken} from "./HzAuth";
import router from '@/router'
import {Decrypt,Encrypt} from "./cryptoJS";
// http默认配置
http.create({
timeout: 10000,
headers:{
'Content-Type' : 'application/json;charset=UTF-8'
}
});
let _this = Vue.prototype;
// let encrypt = new JSEncrypt();
//请求拦截
http.interceptors.request.use(config => {
// console.log(config)
config.data = Encrypt(JSON.stringify(config.data));
// 请求头类型设置为json,因为对于Java后端,application/x-www-form-urlencoded;类型必须使用
// @RequestParam来接收,而不能使用@RequestBody接收
config.headers['Content-Type'] = "application/json;charset=UTF-8";
return config;
}, error => {
return Promise.reject(error);
});
//响应拦截
http.interceptors.response.use(response => {
// console.log(response)
response.data = JSON.parse(Decrypt(response.data));
if(response.status === 200){
switch (response.data.code){
case '0':
if(response.config.method === 'post' && response.data.code === '0'){
_this.$message({message:'操作成功',type:'success'});
}
break;
case '-1':
_this.$message({message:response.data.desc,type:'waring'});
break;
case '-2':
_this.$message({message:response.data.desc,type:'waring'});
router.push('/Login');
break;
case '-3':
_this.$message({message:response.data.desc,type:'waring'});
router.push('/Login');
break;
}
}else {
_this.$toast({message:'请求异常,请重试'});
}
return response;
},error => {
// console.log(error)
switch (error.response.status){
case 500:
_this.$message({message: '服务端异常,请联系技术支持!',type:'error'});
break;
case 503:
_this.$message({message: '系统维护中,请稍后重试!',type:'error'});
break;
case 504:
_this.$message({message: '网络错误,请联系管理员!',type:'error'});
// router.push('Login');
break;
case 404:
_this.$message({message: '系统繁忙,稍后重试!',type:'error'});
break;
default:
_this.$message({message: '系统异常,请稍后重试',type:'error'});
break;
}
return Promise.reject(error.response)
});
export default http;
因为是测试项目,所以对所有的请求和响应都进行了加密和解密
5.测试页面 HelloWorld.vue
<template>
<div>
<input v-model="name"/>
<button @click="createEvent">提交</button>
<button @click="searchEvent">查询</button>
</div>
</template>
<script>
import {create,search} from '../api/api'
export default {
data(){
return{
name:'',
}
},
methods:{
searchEvent(){
search().then(res=>{
console.log(res);
if(res.data.code === '0'){this.name = res.data.data.name}
})
},
createEvent(){
create({name:this.name}).then(res=>{
console.log(res)
})
}
}
}
</script>
<style scoped>
</style>
前端结束,码云地址:https://gitee.com/wuyixue/VUE_AES
后端:
1.新建控制层 controller,实体层 entity ,工具层utils
2.新建实体类UserEntity,工具类{AESUtil,DecodeReqestBodyAdvice,EncodeReponseBodyAdvice,SecretAnnotation},控制类UserController
3.添加依赖至pom文件
4.配置文件application.properties
application.properties
server.port=9095
AES.KEY = aaDJL2d9DfhLZO0z
AES.IV = 412ADDSSFA342442
pom.xml
<?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 http://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.0.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.whd</groupId>
<artifactId>Vue_SpringBoot_AES</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Vue_SpringBoot_AES</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.7</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
</dependency>
<!-- apache base64 -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
</dependency>
<!-- 热部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 自定义注解shiyong -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
UserEntity
package com.whd.entity;
import lombok.Data;
@Data
public class UserEntity {
private String name;
private String id;
}
AESUtil
package com.whd.utils;
/**
* AES 128bit 加密解密工具类
* @author dufy
*/
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
@Component
public class AESUtil {
//使用AES-128-CBC加密模式,key需要为16位,key和iv可以相同,也可以不同!
private static String KEY;
private static String IV;
private static final String CIPHER_ALGORITHM_CBC = "AES/CBC/NoPadding";
/**
* 因为静态变量不能直接从配置文件通过@value获取,所以采用此方法
* @param key
*/
@Value("${AES.KEY}")
public void AESkey(String key){
KEY = key;
}
@Value("${AES.IV}")
public void AESIv(String iv){
IV = iv;
}
/**
* 加密方法 返回base64加密字符串
* 和前端保持一致
* @param data 要加密的数据
* @param key 加密key
* @param iv 加密iv
* @return 加密的结果
* @throws Exception
*/
public static String encrypt(String data, String key, String iv) throws Exception {
try {
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM_CBC);//"算法/模式/补码方式"NoPadding PKCS5Padding
int blockSize = cipher.getBlockSize();
byte[] dataBytes = data.getBytes();
int plaintextLength = dataBytes.length;
if (plaintextLength % blockSize != 0) {
plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
}
byte[] plaintext = new byte[plaintextLength];
System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
byte[] encrypted = cipher.doFinal(plaintext);
return new Base64().encodeToString(encrypted);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 解密方法
* @param data 要解密的数据
* @param key 解密key
* @param iv 解密iv
* @return 解密的结果
* @throws Exception
*/
public static String decrypt(String data, String key, String iv) throws Exception {
try {
// byte[] encrypted1 = new Base64().decode(data);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM_CBC);
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
byte[] original = cipher.doFinal(new Base64().decode(data));
String originalString = new String(original);
return originalString;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 使用默认的key和iv加密
* @param data
* @return
* @throws Exception
*/
public static String encrypt(String data) throws Exception {
return encrypt(data, KEY, IV);
}
/**
* 使用默认的key和iv解密
* @param data
* @return
* @throws Exception
*/
public static String decrypt(String data) throws Exception {
return decrypt(data, KEY, IV);
}
/**
* 测试
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static void main(String args[]) throws Exception {
Map map = new HashMap<>();
map.put("code", "0");
map.put("data", "3232dsfs");
String test1 = JSON.toJSONString(map);
String test =new String(test1.getBytes(),"UTF-8");
String data = null;
data = encrypt(test);
System.out.println("数据:"+test);
System.out.println("加密:"+data);
String jiemi =decrypt(data).trim();
System.out.println("解密:"+jiemi);
}
}
SecretAnnotation
package com.whd.utils;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解
* 加解密
* @author wyx
*
*/
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SecretAnnotation {
/**
* 是否加密
* 默认false
* 加密时传值为true
* @return
*/
boolean encode() default false;
/**
* 是否解密
* 默认为false,
* 解密时传值为true
* @return
*/
boolean decode() default false;
}
DecodeRequesBodyAdvice
package com.whd.utils;
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.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;
import lombok.extern.slf4j.Slf4j;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
/**
* @desc 请求数据解密
*/
@Slf4j
@RestControllerAdvice
public class DecodeRequestBodyAdvice implements RequestBodyAdvice {
@Override
public boolean supports(MethodParameter methodParameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {
//这里设置成false 它就不会再走这个类了
return true;
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
try {
//这个request其实就是入参 可以从这里获取流
//入参放在HttpInputMessage里面 这个方法的返回值也是HttpInputMessage
InputStream inputStream = inputMessage.getBody();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
stringBuilder.append("");
}
} catch (IOException e) {
throw e;
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
throw e;
}
}
}
/**** 解密 */
boolean decode = false;
if (parameter.getMethod().isAnnotationPresent(SecretAnnotation.class)) {
//获取注解配置的包含和去除字段
SecretAnnotation serializedField = parameter.getMethodAnnotation(SecretAnnotation.class);
//出参是否需要加密
decode = serializedField.decode();
}
if(decode){
//获取请求数据
String builderString = stringBuilder.toString();
log.info("【接受的请求数据】",builderString);
try {
String decodeString = AESUtil.decrypt(builderString);
log.info("【解密后的请求数据】",decodeString);
//把数据放到我们封装的对象中
return new MyHttpInputMessage(inputMessage.getHeaders(), new ByteArrayInputStream(decodeString.getBytes("UTF-8")));
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
return null;
}
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {
return body;
}
@Override
public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return body;
}
//这里实现了HttpInputMessage 封装一个自己的HttpInputMessage
static class MyHttpInputMessage implements HttpInputMessage {
HttpHeaders headers;
InputStream body;
public MyHttpInputMessage(HttpHeaders headers, InputStream body) {
this.headers = headers;
this.body = body;
}
@Override
public InputStream getBody() throws IOException {
return body;
}
@Override
public HttpHeaders getHeaders() {
return headers;
}
}
}
EncodeResponseBodyAdvice
package com.whd.utils;
import com.fasterxml.jackson.databind.ObjectMapper;
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.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
/**
* 返回数据加密
*
*/
@SuppressWarnings("rawtypes")
@Slf4j
@RestControllerAdvice
public class EncodeResponseBodyAdvice implements ResponseBodyAdvice {
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
boolean encode = false;
if (methodParameter.getMethod().isAnnotationPresent(SecretAnnotation.class)) {
//获取注解配置的包含和去除字段
SecretAnnotation serializedField = methodParameter.getMethodAnnotation(SecretAnnotation.class);
//出参是否需要加密
encode = serializedField.encode();
}
/**
* 加密开始
*/
if (encode) {
log.info("对接口名为【" + methodParameter.getMethod().getName() + "】返回数据进行加密");
ObjectMapper objectMapper = new ObjectMapper();
try {
String result = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(body);
log.info("加密返回数据 :【" + AESUtil.encrypt(result) + "】");
return AESUtil.encrypt(result);
} catch (Exception e) {
e.printStackTrace();
}
}
return body;
}
}
UserController
package com.whd.controller;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.fastjson.JSON;
import com.whd.entity.UserEntity;
import com.whd.utils.AESUtil;
import com.whd.utils.SecretAnnotation;
@RestController
public class UserController {
/*
* 测试返回数据,会自动加密
* @return
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@RequestMapping(value="/get",method=RequestMethod.GET)
@SecretAnnotation(encode=true)
public Map get(UserEntity entity) throws Exception {
entity.setId("12345");
entity.setName("wexdfs");
Map map = new HashMap();
map.put("code", "0");
map.put("data", entity);
return map;
}
/*
* 自动解密,并将返回信息加密
* @param info
* @return
*/
@SuppressWarnings("rawtypes")
@RequestMapping(value="/save",method=RequestMethod.POST,consumes="application/json;charset=UTF-8")
@SecretAnnotation(decode=true,encode=true)
public Map save(@RequestBody(required=false) UserEntity entity) throws Exception {
System.out.println(entity.getName());
Map<String,String> map = new HashMap<String,String>();
map.put("code", "0");
return map;
}
/**
* 未使用注解的加解密
* @param request
* @return
* @throws Exception
*/
@RequestMapping(value="/save1",method=RequestMethod.POST)
public String save(HttpServletRequest request) throws Exception {
Enumeration<?> e = request.getParameterNames();
String paramName = (String) e.nextElement();
UserEntity entity = JSON.parseObject(AESUtil.decrypt(paramName).trim(),UserEntity.class);
System.out.println(entity.getName());
Map<String,String> map = new HashMap<String,String>();
map.put("code", "0");
return AESUtil.encrypt(JSON.toJSONString(map));
}
}
后端完成,码云地址https://gitee.com/wuyixue/VUE_SPRINGBOOT_AES
整个aes前后端加解密完成,测试使用正常
更多推荐
已为社区贡献13条内容
所有评论(0)