简述:
文章主要讲述了在vue3与springboot交互数据的个人使用的一个加密形式
- SHA256不可逆加密
- AES对称加密
- RSA非对称加密
加密算法就不带大家深入了,对于它的使用文章中有明确的案例
数据加密的大概流程为:(有更优秀的方案可以交流一下)
- 前后端存储一个随机的16长度的字符串作为AES的密钥
- 前端请求后端接口获取被后端使用AES加密后的RSA公钥,前端得到后使用AES密钥解密,然后就可以使用该RSA公钥对敏感数据进行加密处理
- 后端接收到前端加密的数据使用RSA私钥进行解密即可
如果文章存在纰漏,还望赐教一下
下述模块:
一,后端SHA256不可逆加密
二,后端AES对称加密解密
三,后端RSA非对称加密解密
四,前端加密模块(包含js库:crypto-js jsencrypt 对于SHA256加密;AES,RSA加密解密算法的使用)
五,前端全局变量的存储(个人找的方式,不知道大佬们怎末使用的,可以留言教教)
六,前后端加密解密流程
七,前后端调试
一,后端SHA256不可逆加密
不可逆加密
public class SHAUtil {
private static Logger log = LoggerFactory.getLogger(SHAUtil.class);
private SHAUtil() {
}
public static String encrypt(byte[] input) {
try {
byte[] digest = MessageDigest.getInstance("SHA-256").digest(input);
return HexUtils.toHexString(digest);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
}
二,后端AES对称加密解密
对称加密
public class AESUtil {
private static final String AES_ = "AES";
private static Logger log = LoggerFactory.getLogger(AESUtil.class);
private AESUtil() {
}
public static String getKey() {
//return UUID.randomUUID().toString().substring(0,32);//256 使用256需要 在编码解码参数加一个16长度的str为参数
return UUID.randomUUID().toString().substring(0, 16);//128
}
/**
* aes、编码
* @param data 传入需要加密的字符串
* @param aesKey aes的key
* @return 返回base64
*/
public static String encrypt(String data, String aesKey) {
try {
//对密码进行编码
byte[] bytes = aesKey.getBytes(StandardCharsets.UTF_8);
//设置加密算法,生成秘钥
SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_);
// "算法/模式/补码方式"
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec iv = new IvParameterSpec(aesKey.getBytes(StandardCharsets.UTF_8));
//选择加密
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
//根据待加密内容生成字节数组
byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
//返回base64字符串
return Base64Utils.encodeToString(encrypted);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* aes解码
* @param content 传入base64编码的字符串
* @param aesKey aes的key
* @return 返回字符串
*/
public static String decrypt(String content, String aesKey) {
try {
//对密码进行编码
byte[] bytes = aesKey.getBytes(StandardCharsets.UTF_8);
//设置解密算法,生成秘钥
SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_);
//偏移
IvParameterSpec iv = new IvParameterSpec(aesKey.getBytes(StandardCharsets.UTF_8));
// "算法/模式/补码方式"
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//选择解密
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
//先进行Base64解码
byte[] decodeBase64 = Base64Utils.decodeFromString(content);
//根据待解密内容进行解密
byte[] decrypted = cipher.doFinal(decodeBase64);
//将字节数组转成字符串
return new String(decrypted);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
三,后端RSA非对称加密解密
RSA非对称加密(后端代码,公钥解密私钥加密 私钥解密公钥加密 都可行)
对于网上提到的超过长度出错大家可以自行测验
public class RSAUtil {
private static Logger log = LoggerFactory.getLogger(RSAUtil.class);
/**
* 加密算法RSA
*/
private static final String KEY_ALGORITHM = "RSA";
/**
* RSA 位数 如果采用2048 上面最大加密和最大解密则须填写: 245 256
*/
private static final int INITIALIZE_LENGTH = 2048;
/**
* 后端RSA的密钥对(公钥和私钥)Map,由静态代码块赋值
*/
private static final Map<String, String> map = new LinkedHashMap<>(2);
private RSAUtil() {
}
/**
* 生成密钥对(公钥和私钥)
*/
private static void genKeyPair() {
try {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
keyPairGen.initialize(INITIALIZE_LENGTH);
KeyPair keyPair = keyPairGen.generateKeyPair();
// 获取公钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
// 获取私钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
// 得到公钥字符串
String publicKeyString = Base64.encodeBase64String(publicKey.getEncoded());
// 得到私钥字符串
String privateKeyString = Base64.encodeBase64String((privateKey.getEncoded()));
map.put("publicKey", publicKeyString);
map.put("privateKey", privateKeyString);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
public static String getPrivateKey() {
if (map.size() == 0) {
genKeyPair();//初始化生成key
}
return map.get("privateKey");
}
public static String getPublicKey() {
if (map.size() == 0) {
genKeyPair();//初始化生成key
}
return map.get("publicKey");
}
/**
* 公钥解密
*
* @param publicKeyText
* @param text
* @return
* @throws Exception
*/
public static String decryptByPublicKey(String publicKeyText, String text) {
try {
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] result = cipher.doFinal(Base64.decodeBase64(text));
return new String(result);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 私钥加密
*
* @param privateKeyText
* @param text
* @return
* @throws Exception
*/
public static String encryptByPrivateKey(String privateKeyText, String text) {
try {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] result = cipher.doFinal(text.getBytes());
return Base64.encodeBase64String(result);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 私钥解密
*
* @param privateKeyText
* @param text
* @return
* @throws Exception
*/
public static String decryptByPrivateKey(String privateKeyText, String text) {
try {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] result = cipher.doFinal(Base64.decodeBase64(text));
return new String(result);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 公钥加密
*
* @param publicKeyText
* @param text
* @return
*/
public static String encryptByPublicKey(String publicKeyText, String text) {
try {
X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec2);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] result = cipher.doFinal(text.getBytes());
return Base64.encodeBase64String(result);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
四,前端加密模块
前端不太熟练,写了一个js函数代码
注意一下:代码中写到了 私钥加密 公钥解密的代码 但是不能成功,对于该库来说只能公钥加密,私钥解密,满足我们的初步需要,就没有深入解决这个问题(看网上有人改源码,但我相信有现成的库,有的话可以评论区说一下)
import cryptoJs from "crypto-js";
import jsCrypto from "jsencrypt"
import axios from "axios"
/**
* SHA256不可逆加密
* @param {String} data 需要加密的信息
* @returns
*/
function encryptSHA256(data) {
return cryptoJs.SHA256(data).toString(cryptoJs.enc.Hex);
}
/**
* AES加密 对称加密
* @param {String} data 待加密数据
* @param {String} keyStr 密钥可以很长,但是参数中的iv只能是16位,这里不想维护两份所以就用16位长度的字符串作为密钥和iv偏移量
* @returns
*/
function encryptAES(data, keyStr) {
var encrypt = cryptoJs.AES.encrypt(data, cryptoJs.enc.Utf8.parse(keyStr), {
iv: cryptoJs.enc.Utf8.parse(keyStr),
mode: cryptoJs.mode.CBC,
padding: cryptoJs.pad.Pkcs7
}).toString();
return encrypt;
}
/**
* AES解密
* @param {String} data 带解密数据
* @param {String} keyStr 同加密
* @returns
*/
function decryptAES(data, keyStr) {
var decrypt = cryptoJs.AES.decrypt(data, cryptoJs.enc.Utf8.parse(keyStr), {
iv: cryptoJs.enc.Utf8.parse(keyStr),
mode: cryptoJs.mode.CBC,
padding: cryptoJs.pad.Pkcs7
}).toString(cryptoJs.enc.Utf8);
return decrypt;
}
/**
*
* @returns 获得RSA的公钥,私钥
*/
function getRsaKey() {
const encrypt1 = new jsCrypto();
return encrypt1.getKey();
}
/**
* RSA非对称加密 公钥加密
* @param {String} data 待加密数据
* @param {String} publicKey 公钥
* @returns
*/
function encryptByPubKeyRSA(data, publicKey) {
const encrypt1 = new jsCrypto();
encrypt1.setPublicKey(publicKey)
const res = encrypt1.encrypt(data)
return res;
}
/**
* RSA非对称加密 私钥解密
* @param {String} data 待解密数据
* @param {String} privateKey 私钥匙
* @returns
*/
function decryptByPrikeyRSA(data, privateKey) {
const encrypt2 = new jsCrypto();
encrypt2.setPrivateKey(privateKey)
const res = encrypt2.decrypt(data);
return res;
}
/**
* RSA非对称加密 私钥加密
* @param {String} data 待加密数据
* @param {String} privateKey 私钥
* @returns
*/
function encryptByPriKeyRSA(data, privateKey) {
const encrypt1 = new jsCrypto();
encrypt1.setPrivateKey(privateKey)
const res = encrypt1.decrypt(data)
return res;
}
/**
* RSA非对称加密 公钥解密
* @param {String} data 待解密数据
* @param {String} publicKey 公钥
* @returns
*/
function decryptByPubkeyRSA(data, publicKey) {
const encrypt2 = new jsCrypto();
encrypt2.setPublicKey(publicKey)
const res = encrypt2.encrypt(data);
return res;
}
/**
* 先用不可逆加密数据,再使用对称加密
* @param {String} data 待sha+rsa加密数据
* @param {String} rsaPubKey rsa公钥
* @returns
*/
function encryptBySHA256AndRSA(data, rsaPubKey) {
const shaStr = encryptSHA256(data);
console.log("sha 不可逆加密后的密码:", shaStr)
return encryptByPubKeyRSA(shaStr, rsaPubKey);
}
/**
*
* @returns 返回一个promise对象 内包含该次请求的结果 data为被aes加密过的公钥字符串
*/
async function getAesDescRsaPubKey() {
return await axios.get('http://localhost:11111/yyx/security/rsaPubKey')
}
export default {
getRsaKey,getAesDescRsaPubKey, encryptByPubKeyRSA, decryptByPrikeyRSA, encryptByPriKeyRSA, decryptByPubkeyRSA, encryptSHA256, decryptAES, encryptAES, encryptBySHA256AndRSA
}
五,前端全局变量的存储
主要做了一件事,就是存储AES的密钥
创建一个js文件(下面的static可以自定义,在使用的时候调用清楚即可,该方式也可以导入函数到共有区域)
export default (app) => {
console.log("加载公共属性到 $static 模块")
app.config.globalProperties.$static = {
RsaPubKey:"9c81aaf5-b408-49",
}
}
在main.js中导入:
import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import router from './router'
import global from './util/global'//导入
const app = createApp(App)
app.use(ElementPlus)
app.use(router)
global(app)//使用
app.mount('#app')
调用的时候直接:this.$static.AESKey
六,前后端加密解密流程
后端接口两个: server.servlet.context-path: /yyx
- 前端调用查询AES加密的RSA公钥接口
@RestController
@RequestMapping("/security")
@Slf4j
public class SecurityController {
@GetMapping("/rsaPubKey")
public String getRsaPubKey() {
log.info("client 获取 aes desc rsa pub key");
return AESUtil.encrypt(SecurityConfig.getRsaPublicKey(), SecurityConfig.getAesKey());
}
}
- 前端调用注册接口
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
/**
*
*
* @return
*/
@PostMapping("/register")
public CommonResult<Boolean> register(@RequestBody UserDto userDto) {
log.info("注册信息:{}", JSON.toJSONString(userDto));
String password = userDto.getPassword();
String pass = RSAUtil.decryptByPrivateKey(SecurityConfig.getRsaPrivateKey(), password);
log.info("sha 加密的:{}", pass);
return null;
}
public CommonResult<Boolean> login() {
log.info("login");
return null;
}
}
前端逻辑代码vue版本(初学前端)
<template>
<div>
<!-- 其他内容 -->
<h3>注册页</h3>
<form action="login" @submit.prevent="register">
<label for="username">账户:</label>
<input type="text" id="username" v-model="username"><br>
<label for="pass">密码:</label>
<input type="password" id="pass" v-model="password">
<button type="submit">注册</button>
</form>
</div>
</template>
<script>
import axios from "axios"
import crypto from "@/util/crypto";
export default {
data() {
return {
username: "",
password: ""
}
},
methods: {
register() {
//demo版本 调用注册方法,请求后端得到aes加密后的rsa公钥
crypto.getAesDescRsaPubKey().then((aesDescRsaPubKeyData) => {
const aesDescRsaPubKey = aesDescRsaPubKeyData.data;
console.log("aesDescRsaKey:", aesDescRsaPubKey)
//对aes加密过的rsa公钥进行解密,得到rsa公钥
const rsaPubKey = crypto.decryptAES(aesDescRsaPubKey, this.$static.AESKey);
//对密码进行sha256+rsa加密
const enCode = crypto.encryptBySHA256AndRSA(this.password, rsaPubKey);
//真正调用后端的注册方法
this.postRegister(this.username, enCode).then((data1) => {
if (data1.status === 200) {
this.$message({
type: "success",
message: "注册成功"
});
// 跳转
this.$router.push("/index")
}
}).catch(() => {
this.$message({
type: "error",
message: "注册失败"
});
})
}).catch((error) => {
this.$message({
type: "error",
message: "加密解密失败"
});
console.log(error)
})
},
async postRegister(username, enCodePassword) {
return await axios.post("http://localhost:11111/yyx/user/register",
{
username: username,
password: enCodePassword
})
}
}
};
</script>
七,前后端调试
前端记得 npm install 一下,安装一下需要的js库
前端模块给vue界面注册一个路由能展示即可(网上一大片教程)
前后端启动:
npm run dev
传入账号密码:
打控制台
后端收到:
一致,那么我们的加密解密流程就完成了