入参,返参加密

This commit is contained in:
hongchao 2025-04-27 08:59:50 +08:00
parent e58619abef
commit 351d1dac82
5 changed files with 163 additions and 44 deletions

View File

@ -7,6 +7,7 @@
<script> <script>
import ThemePicker from '@/components/ThemePicker' import ThemePicker from '@/components/ThemePicker'
import { getConfigApi } from '@/utils/config'
export default { export default {
name: 'App', name: 'App',
@ -23,6 +24,10 @@ export default {
}, },
} }
}, },
created() {
//
getConfigApi()
},
mounted() { mounted() {
// const script_1 = document.createElement('script') // const script_1 = document.createElement('script')
// const script_2 = document.createElement('script') // const script_2 = document.createElement('script')

View File

@ -112,5 +112,11 @@ export function loginByMall(data) {
}) })
} }
// 获取系统配置
export const getConfig = () => {
return request({
url: '/auth/getConfig',
method: 'get'
})
}

11
src/utils/config.js Normal file
View File

@ -0,0 +1,11 @@
import { getConfig } from '@/api/login'
export function getConfigApi() {
getConfig()
.then((response) => {
console.log(response)
localStorage.setItem('systemConfig', JSON.stringify(response.data))
})
.catch((error) => {
console.error('Failed to fetch config:', error)
})
}

View File

@ -6,7 +6,11 @@ import errorCode from '@/utils/errorCode'
import { tansParams, blobValidate } from '@/utils/ruoyi' import { tansParams, blobValidate } from '@/utils/ruoyi'
import cache from '@/plugins/cache' import cache from '@/plugins/cache'
import { saveAs } from 'file-saver' import { saveAs } from 'file-saver'
import { decryptWithSM4, encryptWithSM4, hashWithSM3AndSalt } from '@/utils/sm'
const systemConfig = JSON.parse(localStorage.getItem('systemConfig')) || {
requestConfig: { encryptRequest: false, checkIntegrity: false, encryptResponse: false }
};
let downloadLoadingInstance let downloadLoadingInstance
// 是否显示重新登录 // 是否显示重新登录
export let isRelogin = { show: false } export let isRelogin = { show: false }
@ -21,12 +25,27 @@ const service = axios.create({
}) })
// request拦截器 // request拦截器
service.interceptors.request.use( service.interceptors.request.use((config) => {
(config) => { // 提取 headers 和方法
const headers = config.headers || {}
const {
encryptRequest = false,
checkIntegrity = false,
encryptResponse = false,
repeatSubmit = false
} = headers
//入参加密
config.headers['encryptRequest'] = systemConfig.requestConfig.encryptRequest && encryptRequest ? 'true' : 'false'
// 数据完整性校验
config.headers['checkIntegrity'] = systemConfig.requestConfig.checkIntegrity && checkIntegrity ? 'true' : 'false'
//回参是否加密
config.headers['encryptResponse'] = systemConfig.requestConfig.encryptResponse && encryptResponse ? 'true' : 'false'
// console.log('🚀 ~ config:', config)
// 是否需要设置 token // 是否需要设置 token
const isToken = (config.headers || {}).isToken === false const isToken = (config.headers || {}).isToken === false
// 是否需要防止数据重复提交 // 是否需要防止数据重复提交
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false const isRepeatSubmit = repeatSubmit
if (getToken() && !isToken) { if (getToken() && !isToken) {
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改 config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
} }
@ -37,10 +56,7 @@ service.interceptors.request.use(
config.params = {} config.params = {}
config.url = url config.url = url
} }
if ( if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
!isRepeatSubmit &&
(config.method === 'post' || config.method === 'put')
) {
const requestObj = { const requestObj = {
url: config.url, url: config.url,
data: data:
@ -49,6 +65,15 @@ service.interceptors.request.use(
: config.data, : config.data,
time: new Date().getTime(), time: new Date().getTime(),
} }
let contentType = config.headers['Content-Type']
// console.log('🚀 ~ contentType:', contentType, requestObj.data)
if (contentType.includes('application/json') && typeof requestObj.data !== 'undefined') {
// 加密数据
if (systemConfig.requestConfig.encryptRequest && encryptRequest) {
// console.log(hashWithSM3AndSalt(requestObj.data));
config.data = encryptWithSM4(requestObj.data+"|"+hashWithSM3AndSalt(requestObj.data))
}
}
const requestSize = Object.keys(JSON.stringify(requestObj)).length // 请求数据大小 const requestSize = Object.keys(JSON.stringify(requestObj)).length // 请求数据大小
const limitSize = 5 * 1024 * 1024 // 限制存放数据5M const limitSize = 5 * 1024 * 1024 // 限制存放数据5M
if (requestSize >= limitSize) { if (requestSize >= limitSize) {
@ -94,6 +119,10 @@ service.interceptors.request.use(
// 响应拦截器 // 响应拦截器
service.interceptors.response.use( service.interceptors.response.use(
(res) => { (res) => {
// console.log('🚀 ~ 响应-res:', res)
if (res.headers.encryptresponse && !res.data.hasOwnProperty('code')) {
res.data = JSON.parse(decryptWithSM4(res.data))
}
// 未设置状态码则默认成功状态 // 未设置状态码则默认成功状态
const code = res.data.code || 200 const code = res.data.code || 200
// 获取错误信息 // 获取错误信息
@ -107,6 +136,11 @@ service.interceptors.response.use(
} }
if (code === 401) { if (code === 401) {
if (!isRelogin.show) { if (!isRelogin.show) {
// 判断当前是否已经在登录页面, 在的话阻止继续执行
if (window.location.href.indexOf('/login') !== -1) {
isRelogin.show = false
return
}
isRelogin.show = true isRelogin.show = true
MessageBox.confirm( MessageBox.confirm(
'登录状态已过期,您可以继续留在该页面,或者重新登录', '登录状态已过期,您可以继续留在该页面,或者重新登录',
@ -169,42 +203,35 @@ service.interceptors.response.use(
// 通用下载方法 // 通用下载方法
export function download(url, params, filename, config) { export function download(url, params, filename, config) {
downloadLoadingInstance = Loading.service({ downloadLoadingInstance = Loading.service({
text: '正在下载数据,请稍候', text: '正在下载数据,请稍候',
spinner: 'el-icon-loading', spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)', background: 'rgba(0, 0, 0, 0.7)'
}) })
return service return service.post(url, params, {
.post(url, params, { transformRequest: [(params) => {
transformRequest: [ return tansParams(params)
(params) => { }],
return tansParams(params) headers: { 'Content-Type': 'application/x-www-form-urlencoded', encryptResponse: false},
}, responseType: 'blob',
], ...config
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, }).then(async(data) => {
responseType: 'blob', const isBlob = blobValidate(data)
...config, if (isBlob) {
}) const blob = new Blob([data])
.then(async (data) => { saveAs(blob, filename)
const isBlob = blobValidate(data) } else {
if (isBlob) { const resText = await data.text()
const blob = new Blob([data]) const rspObj = JSON.parse(resText)
saveAs(blob, filename) const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
} else { Message.error(errMsg)
const resText = await data.text() }
const rspObj = JSON.parse(resText) downloadLoadingInstance.close()
const errMsg = }).catch((r) => {
errorCode[rspObj.code] || rspObj.msg || errorCode['default'] console.error(r)
Message.error(errMsg) Message.error('下载文件出现错误,请联系管理员!')
} downloadLoadingInstance.close()
downloadLoadingInstance.close() })
}) }
.catch((r) => {
console.error(r)
Message.error('下载文件出现错误,请联系管理员!')
downloadLoadingInstance.close()
})
}
// 通用下载方法 // 通用下载方法
export function downloadJson(url, params, filename, config) { export function downloadJson(url, params, filename, config) {
downloadLoadingInstance = Loading.service({ downloadLoadingInstance = Loading.service({
@ -219,7 +246,7 @@ export function downloadJson(url, params, filename, config) {
return params return params
}, },
], ],
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json',encryptResponse: false },
responseType: 'blob', responseType: 'blob',
...config, ...config,
}) })

70
src/utils/sm.js Normal file
View File

@ -0,0 +1,70 @@
// src/utils/encryption.js
import { sm2, sm3, sm4 } from 'sm-crypto'
import SM4 from 'sm-crypto/src/sm4'
// 配置项例如盐值、SM2 公私钥、SM4 密钥
const SM_CONFIG = {
SALT: '2cc0c5f9f1749f1632efa9f63e902323', // SM3 盐值16 字节)
SM4_KEY:"78d1295afa99449b99d6f83820e6965c", // SM4 对称加密密钥
SM4_SALT:"f555adf6c01d0ab0761e626a2dae34a2",
SM2_PUBLIC_KEY: 'your-public-key', // SM2 公钥
SM2_PRIVATE_KEY: 'your-private-key' // SM2 私钥
}
// AES 配置
const AES_CONFIG = {
AES_KEY: 'zhgd@bonus@zhgd@bonus@1234567890', // AES key值
AES_IV: '1234567812345678' // AES 偏移量
}
export function generateUUID() {
// 使用当前时间戳和随机数生成一个 UUID
return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0; // 生成随机数
const v = c === 'x' ? r : (r & 0x3 | 0x8); // 根据 UUID 规范生成相应的值
return v.toString(16); // 转换为十六进制
});
}
// SM3 哈希
export function hashSM3(text) {
// 对数据进行哈希计算
return sm3(text)
}
// 使用 SM3 进行哈希并加入盐值
export function hashWithSM3AndSalt(text) {
// 将文本和盐值拼接在一起
const textWithSalt = SM_CONFIG.SALT + text
// 使用 SM3 进行哈希
return hashSM3(textWithSalt)
}
// SM2 加密
export function encryptWithSM2(text) {
// SM2 公钥加密
return sm2.doEncrypt(text, SM_CONFIG.SM2_PUBLIC_KEY)
}
// SM2 解密
export function decryptWithSM2(encryptedText) {
// SM2 私钥解密
return sm2.doDecrypt(encryptedText, SM_CONFIG.SM2_PRIVATE_KEY)
}
/**
* 加密函数
* @param {string} plainText
* @returns {string} 加密后的密文Hex 编码格式
*/
export function encryptWithSM4(plainText) {
return sm4.encrypt(plainText, SM_CONFIG.SM4_KEY,{ mode: 'cbc', padding: 'pkcs#5',iv:SM_CONFIG.SM4_SALT});
}
/**
* 解密函数
* @param {string} cipherText
* @returns {string} 解密后的明文
*/
export function decryptWithSM4(cipherText){
return SM4.decrypt(cipherText, SM_CONFIG.SM4_KEY,{ mode: 'cbc', padding: 'pkcs#5' ,iv:SM_CONFIG.SM4_SALT});
}