接口加密处理(生产环境)
在实际项目中,对接口请求/返回数据采用RSA加密解密并结合公钥定期更新的方案,需要从加密流程设计、密钥管理、兼容性保障等多维度系统实现。以下从实际应用、拓展方向以及面试应答角度展开说明:
实际项目中的使用方式
实际流程
使用
jsencrypt库实现 RSA 加密,封装公钥管理和加密方法公钥管理
- 公钥通过后端接口获取
- 包含过期时间管理,避免使用过期公钥
- 在需要加密时自动检查公钥有效性,无效则重新获取
Axios 封装
- 通过自定义参数encrypt: true控制是否需要加密
- 只对 POST 请求的请求体进行加密
- 加密后的数据格式为{ encrypted: true, data: '加密字符串' },便于后端识别
使用方式:
- 在 API 请求中添加encrypt: true即可自动加密
- 无需修改组件中的调用方式,保持原有开发体验
创建RSA加密类
js
import JSEncrypt from 'jsencrypt'
/**
* RSA加密工具类
*/
class RSAEncryptor {
constructor() {
this.encryptor = new JSEncrypt()
this.publicKey = null
this.keyExpireTime = null // 公钥过期时间
}
/**
* 设置公钥
* @param {string} publicKey 公钥字符串
* @param {number} expireSeconds 过期时间(秒)
*/
setPublicKey(publicKey, expireSeconds = 3600) {
this.publicKey = publicKey
this.encryptor.setPublicKey(publicKey)
// 设置过期时间
this.keyExpireTime = Date.now() + expireSeconds * 1000
}
/**
* 检查公钥是否有效
* @returns {boolean} 是否有效
*/
isPublicKeyValid() {
return !!this.publicKey && Date.now() < this.keyExpireTime
}
/**
* 加密数据
* @param {any} data 需要加密的数据
* @returns {string|null} 加密后的字符串,失败返回null
*/
encrypt(data) {
if (!this.isPublicKeyValid()) {
console.error('公钥无效或已过期')
return null
}
try {
// 先将数据转为JSON字符串再加密
const jsonStr = JSON.stringify(data)
return this.encryptor.encrypt(jsonStr)
} catch (error) {
console.error('RSA加密失败:', error)
return null
}
}
}
// 导出单例
export const rsaEncryptor = new RSAEncryptor()获取公钥
js
import axios from 'axios'
/**
* 获取后端公钥
* @returns {Promise<{publicKey: string, expireSeconds: number}>}
*/
export const getPublicKey = async () => {
try {
const response = await axios.get('/api/public-key')
// 假设后端返回格式: { publicKey: '...', expireSeconds: 3600 }
if (response.data && response.data.publicKey) {
return response.data
}
throw new Error('获取公钥失败,返回格式不正确')
} catch (error) {
console.error('获取公钥出错:', error)
throw error
}
}封装axios
js
import axios from 'axios'
import { rsaEncryptor } from './rsaEncrypt'
import { getPublicKey } from '../api/keyService'
// 创建axios实例
const service = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API, // 从环境变量获取基础URL
timeout: 10000
})
/**
* 确保公钥已准备好
*/
const ensurePublicKeyReady = async () => {
if (!rsaEncryptor.isPublicKeyValid()) {
try {
const { publicKey, expireSeconds } = await getPublicKey()
rsaEncryptor.setPublicKey(publicKey, expireSeconds)
} catch (error) {
console.error('无法获取有效的公钥,加密功能将不可用')
throw error
}
}
}
// 请求拦截器
service.interceptors.request.use(
async (config) => {
// 检查是否需要加密
const needEncrypt = config.encrypt === true
// 只对POST请求进行加密处理
if (needEncrypt && config.method?.toLowerCase() === 'post' && config.data) {
try {
// 确保公钥可用
await ensurePublicKeyReady()
// 加密数据
const encryptedData = rsaEncryptor.encrypt(config.data)
if (encryptedData) {
// 替换原始数据为加密后的数据
config.data = {
encrypted: true,
data: encryptedData
}
} else {
console.warn('加密失败,将发送原始数据')
}
} catch (error) {
console.error('处理加密时出错:', error)
// 根据实际需求决定是否继续发送请求
// throw error; // 若加密失败则阻止请求发送
}
}
// 删除自定义参数,避免被发送到服务器
delete config.encrypt
return config
},
(error) => {
return Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
(response) => {
return response.data
},
(error) => {
console.error('请求错误:', error)
return Promise.reject(error)
}
)
export default service请求示例
js
import request from '../utils/request'
/**
* 用户登录 (需要加密)
* @param {Object} data 登录信息 {username, password}
*/
export const login = (data) => {
return request({
url: '/user/login',
method: 'post',
data,
encrypt: true // 指定需要加密
})
}
/**
* 获取用户信息 (不需要加密)
*/
export const getUserInfo = () => {
return request({
url: '/user/info',
method: 'get'
})
}
/**
* 更新用户资料 (需要加密)
* @param {Object} data 用户资料
*/
export const updateUserProfile = (data) => {
return request({
url: '/user/profile',
method: 'post',
data,
encrypt: true // 指定需要加密
})
}在vue组件使用
vue
<template>
<div class="login-page">
<form @submit.prevent="handleLogin">
<input v-model="username" type="text" placeholder="用户名" />
<input v-model="password" type="password" placeholder="密码" />
<button type="submit">登录</button>
</form>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { login } from '../api/user'
const username = ref('')
const password = ref('')
const handleLogin = async () => {
try {
const response = await login({
username: username.value,
password: password.value
})
console.log('登录成功:', response)
// 处理登录成功逻辑
} catch (error) {
console.error('登录失败:', error)
}
}
</script>