前后端数据加密代码实战(vue3.4+springboot 2.7.18)

简述:
在这里插入图片描述

文章主要讲述了在vue3与springboot交互数据的个人使用的一个加密形式

  • SHA256不可逆加密
  • AES对称加密
  • RSA非对称加密

加密算法就不带大家深入了,对于它的使用文章中有明确的案例

数据加密的大概流程为:(有更优秀的方案可以交流一下)

  1. 前后端存储一个随机的16长度的字符串作为AES的密钥
  2. 前端请求后端接口获取被后端使用AES加密后的RSA公钥,前端得到后使用AES密钥解密,然后就可以使用该RSA公钥对敏感数据进行加密处理
  3. 后端接收到前端加密的数据使用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

  1. 前端调用查询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());
    }
    
}
  1. 前端调用注册接口
@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
传入账号密码:
打控制台
在这里插入图片描述
后端收到:
在这里插入图片描述
一致,那么我们的加密解密流程就完成了

### 回答1: Vue3和Spring Boot是两种不同的技术,用于前后端分离项目的开发。 Vue3是一种现代化的JavaScript框架,用于构建用户界面。它提供了诸多强大的工具和功能,使开发人员能够快速构建响应式的单页面应用。Vue3具有更高的性能和更好的可维护性,同时还引入了一些新的特性,如Composition API和Teleport等,使开发更加便捷。 Spring Boot是一种用于构建Java后端应用的框架。它提供了一套简化的开发流程,可以快速搭建和配置项目,并提供了丰富的功能来处理数据安全性和其他常见的后端需求。Spring Boot采用了约定优于配置的原则,使得开发人员可以专注于业务逻辑的实现。 在前后端分离项目中,可以通过Vue3来开发前端应用,通过发送HTTP请求来与后端进行通信。后端使用Spring Boot来处理这些请求,处理业务逻辑,并将结果返回给前端。前后端的通信可以使用JSON等数据格式进行交互。 前后端分离项目的优点是可以实现前后端职责的解耦,提高开发效率和可维护性。前端可以专注于用户界面的设计和交互逻辑,后端可以专注于业务逻辑的处理和数据的存储。同时,前后端可以同时进行开发,加快项目的上线速度。 总结来说,使用Vue3和Spring Boot进行前后端分离项目的开发,可以使开发人员能够充分发挥各自的优势,提高开发效率和项目的可维护性。同时,前后端分离项目也能够更好地适应现代化的软件开发需求,提供更好的用户体验和性能。 ### 回答2: Vue3 Spring Boot前后端分离项目是将前端和后端的开发分为两个独立的团队,分别负责开发前端和后端模块,最后通过接口进行数据的交互和通信。 Vue3是一种新一代的JavaScript框架,用于构建用户界面。它建立在Vue.js的基础上,在性能和开发体验上进行了改进。Vue3使用了Composition API,提供了更灵活、可重用和可组合的代码结构,可以更好地管理组件逻辑。 Spring Boot是一个基于Java的开发框架,用于构建独立的、可扩展的和生产就绪的后端应用程序。它大大简化了Java后端的开发流程,提供了自动化配置和快速开发的特性。 在Vue3 Spring Boot前后端分离项目中,前端团队使用Vue3构建用户界面,实现展示数据和交互逻辑。后端团队使用Spring Boot开发RESTful接口,处理前端的请求并进行数据处理和存储。前后端通过接口进行通信,前端发送请求给后端后端返回相应的数据给前端。 前后端分离项目的好处是可以实现前后端的解耦,各自团队可以专注于自己的领域,提高开发效率和协作效果。同时,前后端分离项目也可以使得前端和后端可以独立部署和升级,提高了系统的可维护性和扩展性。 总之,Vue3 Spring Boot前后端分离项目通过利用Vue3和Spring Boot的优势,实现前后端独立开发和交互,提供了更好的开发体验和协作效果。 ### 回答3: Vue3 是一种流行的前端开发框架,而Spring Boot 是一种常用的后端开发框架。在前后端分离的项目中使用 Vue3 和 Spring Boot 可以实现前后端分离的架构。 在这种架构中,前端和后端是独立开发和部署的,彼此通过 RESTful API 进行通信。前端使用 Vue3 来构建用户界面,处理用户交互,并向后端发送请求。后端使用 Spring Boot 来处理请求,执行业务逻辑,并返回相应的数据给前端。 Vue3 提供了强大的数据绑定和组件化的功能,可以方便地构建用户界面,并实现丰富的用户交互。它还提供了一些有用的工具和插件,例如 Vue Router 和 Vuex,用于路由管理和状态管理。通过使用这些功能,我们可以更好地组织和管理前端代码Spring Boot 是一个快速开发和部署的框架,它提供了很多现成的功能和插件,使后端开发更加高效。使用 Spring Boot,我们可以很方便地定义 RESTful API,处理请求和返回响应。它还提供了许多有用的功能,例如数据库访问、安全认证和日志记录。 在 Vue3 和 Spring Boot 的配合下,我们可以实现前后端分离的开发模式,提高开发效率和代码质量。前端和后端开发人员可以并行工作,互不干扰。同时,前后端分离的架构也能为项目带来更好的可维护性和可扩展性。因此,Vue3 和 Spring Boot 的组合是一个理想的选择,用于构建现代化的前后端分离项目。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值