前后端对称加密AES的使用
2025/5/29...大约 3 分钟
前后端对称加密AES的使用
场景
- 需要在前端填写密码,将密码发送给后端,后端保存密码。
- 前端请求这个密码,后端将密码从数据库读出返回给前端
前端使用vue2+vite、后端使用Springboot、数据库使用MySQL
详细流程
安装crypto-js
npm install crypto-js --save-dev
配置文件
# .env.development 文件
# 密钥管理加密密钥
VITE_MANAGEMENT_KEY = "bHZ7yzTl2xjpuNev"
编写JS文件
import CryptoJS from 'crypto-js'
export default {
// 随机生成指定数量的16进制key
generatekey(num) {
var library = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let key = '';
for (var i = 0; i < num; i++) {
var randomPoz = Math.floor(Math.random() * library.length)
key += library.substring(randomPoz, randomPoz + 1)
}
return key
},
// 加密
encrypt(keyStr, word) {
let key = CryptoJS.enc.Utf8.parse(keyStr)
let content = CryptoJS.enc.Utf8.parse(word)
let encrypted = CryptoJS.AES.encrypt(content, key, {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7})
return encrypted.toString()
},
// 解密
decrypt(keyStr,word) {
let key = CryptoJS.enc.Utf8.parse(keyStr);
let decrypt = CryptoJS.AES.decrypt(word, key, {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7})
return CryptoJS.enc.Utf8.stringify(decrypt).toString()
}
}
前端加密
// 提交接口
async handleSubmit() {
try {
// 配置文件获取密钥
let key = import.meta.env.VITE_MANAGEMENT_KEY
// 在需要加密的位置使用
await updateInsertByBo({
amapWebKey: AES.encrypt(key, this.formData.amapWebKey),
amapServerKey: AES.encrypt(key, this.formData.amapServerKey),
baseUrl: AES.encrypt(key, this.formData.baseUrl),
apiKey: AES.encrypt(key, this.formData.apiKey),
completionsPath: AES.encrypt(key, this.formData.completionsPath),
model: AES.encrypt(key, this.formData.model),
})
this.$message.success('保存成功')
// 等待500ms
await new Promise(resolve => setTimeout(resolve, 500))
// 刷新页面
this.$router.go(0)
} catch (error) {
}
}
// 接口js文件
// 修改密钥管理
export function updateInsertByBo(data) {
return request({
url: serverName + '/view/keyManagement',
method: 'put',
data: data
})
}
后端接收保存
Controller.java
@Operation(summary = "修改密钥")
@RepeatSubmit()
@PutMapping()
public R<Void> updateInsertByBo(@Validated(EditGroup.class) @RequestBody ViewKeyManagementBo bo) {
return toAjax(iViewKeyManagementService.updateInsertByBo(bo) ? 1 : 0);
}
Service.java
// 配置文件读取前端对称密钥,用于前端信息加解密
@Value("${management.webKey:}")
private String webKey;
// 配置文件读取后端对称密钥,用于数据库存储信息加解密
@Value("${management.serviceKey:}")
private String serviceKey;
@Override
public Boolean updateInsertByBo(ViewKeyManagementBo bo) {
Long userId = AuthUtil.getUserId();
if (userId < 0L) {
throw new ServiceException("请刷新页面,登陆后重试");
}
LambdaQueryWrapper<ViewKeyManagement> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.eq(ViewKeyManagement::getUserId, userId);
queryWrapper.eq(ViewKeyManagement::getStatus, EnumStatus.RUN.getIndex());
ViewKeyManagement keyManagement = baseMapper.selectOne(queryWrapper);
ViewKeyManagement update = new ViewKeyManagement();
// 先解密,再加密。用不同密钥
update.setAmapServerKey(decryptAndEncrypt(bo.getAmapServerKey(), webKey, serviceKey));
update.setAmapWebKey(decryptAndEncrypt(bo.getAmapWebKey(), webKey, serviceKey));
update.setApiKey(decryptAndEncrypt(bo.getApiKey(), webKey, serviceKey));
update.setCompletionsPath(decryptAndEncrypt(bo.getCompletionsPath(), webKey, serviceKey));
update.setModel(decryptAndEncrypt(bo.getModel(), webKey, serviceKey));
update.setBaseUrl(decryptAndEncrypt(bo.getBaseUrl(), webKey, serviceKey));
if (keyManagement != null) {
update.setId(keyManagement.getId());
return baseMapper.updateById(update) > 0;
} else {
update.setUserId(userId);
return baseMapper.insert(update) > 0;
}
}
/**
* 解密并加密
*
* @param content 内容
* @param decryptKey 解密密钥
* @param encryptKey 加密密钥
* @return 解密并加密后的内容
*/
private String decryptAndEncrypt(String content, String decryptKey, String encryptKey) {
if (StringUtils.isBlank(content)) {
return null;
}
if (StringUtils.isBlank(decryptKey) || StringUtils.isBlank(encryptKey)) {
throw new ServiceException("请配置密钥");
}
String decryptStr = SecureUtil.aes(decryptKey.getBytes()).decryptStr(content);
return SecureUtil.aes(encryptKey.getBytes()).encryptBase64(decryptStr);
}
后端相应保存结果给前端
Controller.java
@Operation(summary = "获取密钥详细信息")
@SaCheckPermission("view:keyManagement:query")
@GetMapping("/queryInfoByUserId")
public R<ViewKeyManagement> queryInfoByUserId() {
ViewKeyManagement domain = iViewKeyManagementService.queryInfoByUserId();
return R.ok(domain);
}
Service.java
// 配置文件读取前端对称密钥,用于前端信息加解密
@Value("${management.webKey:}")
private String webKey;
// 配置文件读取后端对称密钥,用于数据库存储信息加解密
@Value("${management.serviceKey:}")
private String serviceKey;
@Override
public ViewKeyManagement queryInfoByUserId() {
Long userId = AuthUtil.getUserId();
if (userId < 0L) {
throw new ServiceException("请刷新页面,登陆后重试");
}
// 获取管理员账号配置的配置文件
LambdaQueryWrapper<ViewKeyManagement> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.eq(ViewKeyManagement::getUserId, iUserRoleService.getAdminUserId());
queryWrapper.eq(ViewKeyManagement::getStatus, EnumStatus.RUN.getIndex());
ViewKeyManagement keyManagement = baseMapper.selectOne(queryWrapper);
if (keyManagement != null) {
// 使用对称加密,解密再加密
keyManagement.setAmapServerKey(decryptAndEncrypt(keyManagement.getAmapServerKey(), serviceKey, webKey));
keyManagement.setAmapWebKey(decryptAndEncrypt(keyManagement.getAmapWebKey(), serviceKey, webKey));
keyManagement.setApiKey(decryptAndEncrypt(keyManagement.getApiKey(), serviceKey, webKey));
keyManagement.setCompletionsPath(decryptAndEncrypt(keyManagement.getCompletionsPath(), serviceKey, webKey));
keyManagement.setModel(decryptAndEncrypt(keyManagement.getModel(), serviceKey, webKey));
keyManagement.setBaseUrl(decryptAndEncrypt(keyManagement.getBaseUrl(), serviceKey, webKey));
return keyManagement;
}
return null;
}
前端获取保存结果
// .vue -> method
async loadConfig() {
try {
let key = import.meta.env.VITE_MANAGEMENT_KEY
const res = await queryInfoByUserId()
if (res && res.code === 200 && res.data) {
this.formData.amapWebKey = AES.decrypt(key, res.data.amapWebKey)
this.formData.amapServerKey = AES.decrypt(key, res.data.amapServerKey)
this.formData.baseUrl = AES.decrypt(key, res.data.baseUrl)
this.formData.apiKey = AES.decrypt(key, res.data.apiKey)
this.formData.completionsPath = AES.decrypt(key, res.data.completionsPath)
this.formData.model = AES.decrypt(key, res.data.model)
}
} catch (error) {
}
}
// 请求接口js文件,查询密钥管理详细
export function queryInfoByUserId() {
return request({
url: serverName + '/view/keyManagement/queryInfoByUserId',
method: 'get'
})
}