简述:
文章主要讲述了在vue3与springboot交互数据的个人使用的一个加密形式
- SHA256不可逆加密
- AES对称加密
- RSA非对称加密
加密算法就不带大家深入了,对于它的使用文章中有明确的案例
数据加密的大概流程为:(有更优秀的方案可以交流一下)
- 前后端存储一个随机的16长度的字符串作为AES的密钥
- 前端请求后端接口获取被后端使用AES加密后的RSA公钥,前端得到后使用AES解密,然后就可以使用该公钥对敏感数据进行加密处理
- 后端接收到加密的数据使用私钥进行解密即可
如果文章存在纰漏,还望赐教一下
下述模块:
一,后端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
传入账号密码:
打控制台
后端收到:
一致,那么我们的加密解密流程就完成了