前后端分离数据加密传输方案(Vue + Java)
推荐方案
对于Vue前端和Java后端的分离架构,推荐以下数据加密传输方案:
HTTPS:基础安全层,必须启用
AES对称加密:对敏感数据进行加密
RSA非对称加密:用于传输AES密钥
数据签名:防止数据篡改
完整解决方案
1. 准备工作
确保服务器已配置HTTPS
后端生成RSA密钥对(公钥和私钥)
前端通过安全接口获取RSA公钥
2. 加密流程
前端生成随机AES密钥
使用RSA公钥加密AES密钥
使用AES密钥加密请求数据
将加密后的AES密钥和加密数据一起发送到后端
后端使用RSA私钥解密AES密钥,再用AES密钥解密数据
3. 前端实现(Vue示例)
首先安装加密库:
npm install crypto-js jsencrypt
加密工具类 src/utils/crypto.js
import CryptoJS from 'crypto-js'import JSEncrypt from 'jsencrypt'// 存储RSA公钥let publicKey = ''// 设置RSA公钥export function setPublicKey(key) { publicKey = key}// 生成随机AES密钥export function generateAesKey() { const key = CryptoJS.lib.WordArray.random(32).toString() const iv = CryptoJS.lib.WordArray.random(16).toString() return { key, iv }}// RSA加密export function rsaEncrypt(data) { const encryptor = new JSEncrypt() encryptor.setPublicKey(publicKey) return encryptor.encrypt(data)}// AES加密export function aesEncrypt(data, key, iv) { const keyHex = CryptoJS.enc.Utf8.parse(key) const ivHex = CryptoJS.enc.Utf8.parse(iv) const encrypted = CryptoJS.AES.encrypt(data, keyHex, { iv: ivHex, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }) return encrypted.toString()}// 封装加密请求数据export function encryptRequestData(data) { // 生成AES密钥 const { key, iv } = generateAesKey() // 加密数据 const encryptedData = aesEncrypt(JSON.stringify(data), key, iv) // 加密AES密钥 const encryptedKey = rsaEncrypt(key) const encryptedIv = rsaEncrypt(iv) return { key: encryptedKey, iv: encryptedIv, data: encryptedData }}
API请求封装 src/api/request.js
import axios from 'axios'import { encryptRequestData } from '@/utils/crypto'// 创建axios实例const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API, timeout: 10000})// 请求拦截器 - 加密数据service.interceptors.request.use( async config => { // 获取RSA公钥(可以从store或localStorage获取) const publicKey = localStorage.getItem('rsaPublicKey') // 如果是POST/PUT请求且需要加密 if (['post', 'put'].includes(config.method) && config.needEncrypt) { const encryptedData = encryptRequestData(config.data) config.data = encryptedData } return config }, error => { return Promise.reject(error) })// 响应拦截器 - 解密数据service.interceptors.response.use( response => { // 如果需要解密,可以在这里处理 // const decryptedData = decryptResponseData(response.data) return response.data }, error => { return Promise.reject(error) })export default service
获取公钥并存储 src/api/auth.js
import request from './request'import { setPublicKey } from '@/utils/crypto'export function getPublicKey() { return request({ url: '/auth/publicKey', method: 'get', needEncrypt: false }).then(response => { // 存储公钥 setPublicKey(response.data) localStorage.setItem('rsaPublicKey', response.data) return response })}
使用示例 src/api/user.js
import request from './request'export function login(username, password) { return request({ url: '/user/login', method: 'post', data: { username, password }, needEncrypt: true })}export function getUserInfo() { return request({ url: '/user/info', method: 'get', needEncrypt: false })}
4. 后端实现(Java)
RSA密钥对生成
import javax.crypto.Cipher;import java.security.*;import java.security.spec.PKCS8EncodedKeySpec;import java.security.spec.X509EncodedKeySpec;import java.util.Base64;public class RSAUtil { // 生成RSA密钥对 public static KeyPair generateKeyPair() throws NoSuchAlgorithmException { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(2048); return keyPairGenerator.generateKeyPair(); } // 获取公钥(Base64编码) public static String getPublicKey(KeyPair keyPair) { PublicKey publicKey = keyPair.getPublic(); return Base64.getEncoder().encodeToString(publicKey.getEncoded()); } // 获取私钥(Base64编码) public static String getPrivateKey(KeyPair keyPair) { PrivateKey privateKey = keyPair.getPrivate(); return Base64.getEncoder().encodeToString(privateKey.getEncoded()); } // RSA解密 public static String decrypt(String encryptedText, String privateKey) throws Exception { byte[] encryptedBytes = Base64.getDecoder().decode(encryptedText); byte[] privateKeyBytes = Base64.getDecoder().decode(privateKey); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey key = keyFactory.generatePrivate(keySpec); Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, key); byte[] decryptedBytes = cipher.doFinal(encryptedBytes); return new String(decryptedBytes); }}
AES工具类
import javax.crypto.Cipher;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import java.util.Base64;public class AESUtil { // AES解密 public static String decrypt(String encryptedData, String key, String iv) throws Exception { byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData); byte[] keyBytes = key.getBytes("UTF-8"); byte[] ivBytes = iv.getBytes("UTF-8"); SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES"); IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec); byte[] decryptedBytes = cipher.doFinal(encryptedBytes); return new String(decryptedBytes, "UTF-8"); }}
Spring Controller示例
@RestController@RequestMapping("/api")public class ApiController { @GetMapping("/publicKey") public ResponseEntity<String> getPublicKey() { // 从配置或缓存中获取公钥 String publicKey = "你的RSA公钥"; return ResponseEntity.ok(publicKey); } @PostMapping("/login") public ResponseEntity<?> login(@RequestBody EncryptedRequest request) { try { // 1. 解密AES密钥 String aesKey = RSAUtil.decrypt(request.getKey(), "你的RSA私钥"); String aesIv = RSAUtil.decrypt(request.getIv(), "你的RSA私钥"); // 2. 解密数据 String decryptedData = AESUtil.decrypt(request.getData(), aesKey, aesIv); // 3. 转换为对象 LoginRequest loginRequest = new ObjectMapper().readValue(decryptedData, LoginRequest.class); // 4. 处理业务逻辑... return ResponseEntity.ok("登录成功"); } catch (Exception e) { return ResponseEntity.status(500).body("解密失败"); } } // 加密请求体 static class EncryptedRequest { private String key; private String iv; private String data; // getters and setters } // 实际请求体 static class LoginRequest { private String username; private String password; // getters and setters }}
安全建议
定期更换RSA密钥对:建议每天或每周更换一次
前端不要存储私钥:私钥永远只存在于后端
敏感操作二次验证:如支付等操作可以要求用户输入短信验证码
防重放攻击:可以加入时间戳和随机数,后端校验请求的有效期
数据签名:对传输的数据进行签名,防止篡改
性能考虑
RSA加密较慢,只用于加密AES密钥
AES加密速度快,用于加密实际数据
对于不敏感的数据,可以不加密以提升性能
这种方案在安全性和性能之间取得了良好的平衡,适合大多数Web应用场景。
版权声明:
本站转载作品版权归原作者及来源网站所有,原创内容作品版权归作者所有,任何内容转载、商业用途等均须联系原作者并注明来源。如果侵犯了您的权益请与我联系,我将在24小时内删除。