前后端分离数据加密传输方案(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小时内删除。