接口加密
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
import useUserStore from "../stores/useUserStore";
|
import useUserStore from "../stores/useUserStore";
|
||||||
|
import { sm4Encrypt, sm4Decrypt } from '@/utils/crypto';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
request,
|
request,
|
||||||
createRequest,
|
createRequest,
|
||||||
@@ -384,6 +386,14 @@ const formatTotal = (total) => {
|
|||||||
return `${roundedTotal}+`;
|
return `${roundedTotal}+`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function sm2_Decrypt(word, key) {
|
||||||
|
return SM.decrypt(word, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sm2_Encrypt(word, key) {
|
||||||
|
return SM.encrypt(word, key);
|
||||||
|
}
|
||||||
|
|
||||||
export function formatDate(isoString) {
|
export function formatDate(isoString) {
|
||||||
const date = new Date(isoString);
|
const date = new Date(isoString);
|
||||||
const year = date.getFullYear();
|
const year = date.getFullYear();
|
||||||
@@ -1041,4 +1051,8 @@ export default {
|
|||||||
insertSortData,
|
insertSortData,
|
||||||
isInWechatMiniProgramWebview,
|
isInWechatMiniProgramWebview,
|
||||||
isEmptyObject,
|
isEmptyObject,
|
||||||
|
sm4Decrypt,
|
||||||
|
sm4Encrypt,
|
||||||
|
sm2_Decrypt,
|
||||||
|
sm2_Encrypt,
|
||||||
}
|
}
|
||||||
|
|||||||
13
config.js
13
config.js
@@ -6,8 +6,8 @@
|
|||||||
*/
|
*/
|
||||||
export default {
|
export default {
|
||||||
// baseUrl: 'http://39.98.44.136:8080', // 测试
|
// baseUrl: 'http://39.98.44.136:8080', // 测试
|
||||||
baseUrl: 'https://www.xjksly.cn/api/ks', // 正式环境
|
// baseUrl: 'https://www.xjksly.cn/api/ks', // 正式环境
|
||||||
// baseUrl: 'http://ks.zhaopinzao8dian.com/api/ks', // 测试
|
baseUrl: 'http://ks.zhaopinzao8dian.com/api/ks', // 测试
|
||||||
|
|
||||||
// LCBaseUrl:'http://10.110.145.145:9100',//内网端口
|
// LCBaseUrl:'http://10.110.145.145:9100',//内网端口
|
||||||
// LCBaseUrlInner:'http://10.110.145.145:10100',//招聘、培训、帮扶
|
// LCBaseUrlInner:'http://10.110.145.145:10100',//招聘、培训、帮扶
|
||||||
@@ -88,5 +88,12 @@ export default {
|
|||||||
title: '找工作,用 AI 更高效|青岛市智能求职平台',
|
title: '找工作,用 AI 更高效|青岛市智能求职平台',
|
||||||
desc: '融合海量岗位、智能简历匹配、竞争力分析,助你精准锁定理想职位!',
|
desc: '融合海量岗位、智能简历匹配、竞争力分析,助你精准锁定理想职位!',
|
||||||
imgUrl: 'https://qd.zhaopinzao8dian.com/file/csn/qd_shareLogo.jpg',
|
imgUrl: 'https://qd.zhaopinzao8dian.com/file/csn/qd_shareLogo.jpg',
|
||||||
}
|
},
|
||||||
|
// SM4 加密配置
|
||||||
|
sm4Config: {
|
||||||
|
key: '86C63180C1306ABC4D8F989E0A0BC9F3',
|
||||||
|
mode: 'ECB',
|
||||||
|
iv: 'UISwD9fW6cFh9SNS',
|
||||||
|
cipherType: 'base64',
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
37
utils/crypto.js
Normal file
37
utils/crypto.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { sm4 } from 'sm-crypto';
|
||||||
|
|
||||||
|
export function sm4Decrypt(key, value, mode = "hex") {
|
||||||
|
try {
|
||||||
|
if (key.length !== 32) {
|
||||||
|
uni.showToast({ title: '密钥必须是32位16进制字符串(128位)', icon: 'none' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const decrypted = sm4.decrypt(value, key, {
|
||||||
|
mode: 'ecb',
|
||||||
|
cipherType: mode === 'hex' ? 'hex' : 'base64',
|
||||||
|
padding: 'pkcs#5'
|
||||||
|
});
|
||||||
|
return decrypted;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('sm4 decrypt error:', e);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sm4Encrypt(key, value, mode = "hex") {
|
||||||
|
try {
|
||||||
|
if (key.length !== 32) {
|
||||||
|
uni.showToast({ title: '密钥必须是32位16进制字符串(128位)', icon: 'none' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const encrypted = sm4.encrypt(value, key, {
|
||||||
|
mode: 'ecb',
|
||||||
|
cipherType: mode === 'hex' ? 'hex' : 'base64',
|
||||||
|
padding: 'pkcs#5'
|
||||||
|
});
|
||||||
|
return encrypted;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('sm4 encrypt error:', e);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,66 @@
|
|||||||
import config from "@/config.js"
|
import config from "@/config.js"
|
||||||
|
import { sm4Encrypt, sm4Decrypt } from '@/utils/crypto';
|
||||||
import useUserStore from '@/stores/useUserStore';
|
import useUserStore from '@/stores/useUserStore';
|
||||||
|
|
||||||
|
const needToEncryptSet = new Set([
|
||||||
|
'POST:/app/login',
|
||||||
|
'GET:/app/user/resume',
|
||||||
|
'POST:/app/user/resume',
|
||||||
|
'POST:/app/user/experience/edit',
|
||||||
|
'POST:/app/user/experience/delete',
|
||||||
|
'GET:/app/user/experience/getSingle',
|
||||||
|
'GET:/app/user/experience/list',
|
||||||
|
'POST:/app/user/cert',
|
||||||
|
'POST:/app/user/getUserArchives',
|
||||||
|
]);
|
||||||
|
|
||||||
|
const encryptPathPrefixes = [
|
||||||
|
'/app/common/',
|
||||||
|
'/app/chat/',
|
||||||
|
'/app/speech/',
|
||||||
|
'/app/job/',
|
||||||
|
'/app/company/',
|
||||||
|
];
|
||||||
|
|
||||||
|
const isEncryptNeeded = (method, url) => {
|
||||||
|
const key = `${method.toUpperCase()}:${url}`;
|
||||||
|
if (needToEncryptSet.has(key)) return true;
|
||||||
|
for (const encryptKey of needToEncryptSet) {
|
||||||
|
const [encryptMethod, encryptUrl] = encryptKey.split(':');
|
||||||
|
if (encryptMethod === method.toUpperCase() && url.startsWith(encryptUrl.split('/{')[0])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const prefix of encryptPathPrefixes) {
|
||||||
|
if (url.startsWith(prefix)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const encryptRequestData = (data) => {
|
||||||
|
const jsonData = JSON.stringify(data);
|
||||||
|
// const jsonData = JSON.stringify({a: '1'});
|
||||||
|
console.log('[请求] 加密前:', jsonData)
|
||||||
|
return {
|
||||||
|
encrypted: true,
|
||||||
|
encryptedData: sm4Encrypt(config.sm4Config.key, jsonData),
|
||||||
|
timestamp: Date.now(),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleResponseData = (resData) => {
|
||||||
|
try {
|
||||||
|
if (resData?.encrypted) {
|
||||||
|
const decrypted = sm4Decrypt(config.sm4Config.key, resData.encryptedData);
|
||||||
|
resData = JSON.parse(decrypted);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[请求] 解密失败:', e.message);
|
||||||
|
}
|
||||||
|
return resData;
|
||||||
|
};
|
||||||
export function request({
|
export function request({
|
||||||
url,
|
url,
|
||||||
method = 'GET',
|
method = 'GET',
|
||||||
@@ -89,25 +150,27 @@ export function createRequest(url, data = {}, method = 'GET', loading = false, h
|
|||||||
if(needHeader){
|
if(needHeader){
|
||||||
header["Authorization"] = encodeURIComponent(Authorization);
|
header["Authorization"] = encodeURIComponent(Authorization);
|
||||||
}
|
}
|
||||||
|
const requestData = isEncryptNeeded(method, url) ? encryptRequestData(data) : data;
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
uni.request({
|
uni.request({
|
||||||
url: config.baseUrl + url,
|
url: config.baseUrl + url,
|
||||||
method: method,
|
method: method,
|
||||||
data: data,
|
data: requestData,
|
||||||
header,
|
header,
|
||||||
success: resData => {
|
success: resData => {
|
||||||
// 响应拦截
|
// 响应拦截
|
||||||
if (resData.statusCode === 200) {
|
if (resData.statusCode === 200) {
|
||||||
|
const responseData = handleResponseData(resData.data)
|
||||||
const {
|
const {
|
||||||
code,
|
code,
|
||||||
msg
|
msg
|
||||||
} = resData.data
|
} = responseData
|
||||||
if (code === 200) {
|
if (code === 200) {
|
||||||
resolve(resData.data)
|
resolve(responseData)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 处理业务错误
|
// 处理业务错误
|
||||||
if (resData.data?.code === 401 || resData.data?.code === 402) {
|
if (responseData?.code === 401 || responseData?.code === 402) {
|
||||||
useUserStore().logOut()
|
useUserStore().logOut()
|
||||||
}
|
}
|
||||||
// 显示具体的错误信息
|
// 显示具体的错误信息
|
||||||
|
|||||||
Reference in New Issue
Block a user