防重放攻击
This commit is contained in:
parent
3320a56495
commit
1ea005424a
|
|
@ -28,6 +28,7 @@
|
|||
"axios": "0.28.1",
|
||||
"clipboard": "2.0.8",
|
||||
"core-js": "3.37.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
"echarts": "5.4.0",
|
||||
"element-ui": "2.15.14",
|
||||
"file-saver": "2.0.5",
|
||||
|
|
|
|||
22
src/main.js
22
src/main.js
|
|
@ -75,6 +75,28 @@ Vue.use(Element, {
|
|||
|
||||
Vue.config.productionTip = false
|
||||
|
||||
// 防F12开发者工具 - Vue版本
|
||||
/* Vue.mixin({
|
||||
mounted() {
|
||||
this.preventDevTools()
|
||||
},
|
||||
methods: {
|
||||
preventDevTools() {
|
||||
const banDevTools = () => {
|
||||
setInterval(() => {
|
||||
debugger
|
||||
}, 50)
|
||||
}
|
||||
|
||||
try {
|
||||
banDevTools()
|
||||
} catch (err) {
|
||||
console.warn('防开发者工具功能初始化失败:', err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}) */
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
router,
|
||||
|
|
|
|||
|
|
@ -52,5 +52,5 @@ module.exports = {
|
|||
/**
|
||||
* 底部版权文本内容
|
||||
*/
|
||||
footerContent: 'Copyright © 2018-2025 RuoYi. All Rights Reserved.'
|
||||
footerContent: ''
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import router from '@/router'
|
||||
import { MessageBox, } from 'element-ui'
|
||||
import { login, logout, getInfo } from '@/api/login'
|
||||
import { getToken, setToken, removeToken } from '@/utils/auth'
|
||||
import { getToken, setToken, removeToken,setUserId,removeUserId,setSecretKey,removeSecretKey } from '@/utils/auth'
|
||||
import { isHttp, isEmpty } from "@/utils/validate"
|
||||
import defAva from '@/assets/images/profile.jpg'
|
||||
import { encryptWithSM4} from '@/utils/sm'
|
||||
|
||||
const user = {
|
||||
state: {
|
||||
|
|
@ -63,6 +64,8 @@ const user = {
|
|||
return new Promise((resolve, reject) => {
|
||||
getInfo().then(res => {
|
||||
const user = res.user
|
||||
setUserId(encryptWithSM4(user.userId + ''))
|
||||
setSecretKey(encryptWithSM4(user.secret))
|
||||
let avatar = user.avatar || ""
|
||||
if (!isHttp(avatar)) {
|
||||
avatar = (isEmpty(avatar)) ? defAva : process.env.VUE_APP_BASE_API + avatar
|
||||
|
|
@ -104,6 +107,8 @@ const user = {
|
|||
commit('SET_ROLES', [])
|
||||
commit('SET_PERMISSIONS', [])
|
||||
removeToken()
|
||||
removeUserId()
|
||||
removeSecretKey()
|
||||
resolve()
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
|
|
@ -116,6 +121,8 @@ const user = {
|
|||
return new Promise(resolve => {
|
||||
commit('SET_TOKEN', '')
|
||||
removeToken()
|
||||
removeUserId()
|
||||
removeSecretKey()
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import Cookies from 'js-cookie'
|
||||
|
||||
const TokenKey = 'Admin-Token'
|
||||
const UserIdKey = 'UserId'
|
||||
const SecretKey = 'Secret'
|
||||
|
||||
export function getToken() {
|
||||
return Cookies.get(TokenKey)
|
||||
|
|
@ -13,3 +15,27 @@ export function setToken(token) {
|
|||
export function removeToken() {
|
||||
return Cookies.remove(TokenKey)
|
||||
}
|
||||
|
||||
export function getUserId() {
|
||||
return Cookies.get(UserIdKey)
|
||||
}
|
||||
|
||||
export function setUserId(token) {
|
||||
return Cookies.set(UserIdKey, token)
|
||||
}
|
||||
|
||||
export function removeUserId() {
|
||||
return Cookies.remove(UserIdKey)
|
||||
}
|
||||
|
||||
export function getSecretKey() {
|
||||
return Cookies.get(SecretKey)
|
||||
}
|
||||
|
||||
export function setSecretKey(token) {
|
||||
return Cookies.set(SecretKey, token)
|
||||
}
|
||||
|
||||
export function removeSecretKey() {
|
||||
return Cookies.remove(SecretKey)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
import CryptoJS from 'crypto-js'
|
||||
|
||||
// HMAC-SHA256加密
|
||||
export function hmacSHA256(message, secret) {
|
||||
return CryptoJS.HmacSHA256(message, secret).toString(CryptoJS.enc.Hex)
|
||||
}
|
||||
|
||||
// 生成请求签名
|
||||
export function generateRequestSignature(userId, timestamp, method, url, secret) {
|
||||
const signString = userId + timestamp + method.toUpperCase() + url
|
||||
console.log(signString);
|
||||
return hmacSHA256(signString, secret)
|
||||
}
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
import axios from 'axios'
|
||||
import { Notification, MessageBox, Message, Loading } from 'element-ui'
|
||||
import store from '@/store'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import { getToken,getUserId,getSecretKey } from '@/utils/auth'
|
||||
import errorCode from '@/utils/errorCode'
|
||||
import { tansParams, blobValidate } from '@/utils/bonus'
|
||||
import cache from '@/plugins/cache'
|
||||
import { saveAs } from 'file-saver'
|
||||
import { decryptWithSM4, encryptWithSM4, hashWithSM3AndSalt } from '@/utils/sm'
|
||||
import {generateRequestSignature } from '@/utils/crypto-js'
|
||||
|
||||
const systemConfig = {
|
||||
requestConfig: {
|
||||
|
|
@ -36,6 +37,7 @@ service.interceptors.request.use(
|
|||
checkIntegrity = true,
|
||||
encryptResponse = true,
|
||||
repeatSubmit = false,
|
||||
skipReplayProtection = false
|
||||
} = headers
|
||||
|
||||
// 设置请求头
|
||||
|
|
@ -50,12 +52,61 @@ service.interceptors.request.use(
|
|||
config.headers['Authorization'] = 'Bearer ' + getToken()
|
||||
}
|
||||
|
||||
// 添加防重放签名头(如果不是跳过重放保护的请求)
|
||||
if (!skipReplayProtection) {
|
||||
try {
|
||||
const userId = getUserId()
|
||||
const userSecret = getSecretKey()
|
||||
const method = config.method.toUpperCase()
|
||||
const timestamp = Date.now().toString()
|
||||
|
||||
let requestUrl = config.url
|
||||
|
||||
if (config.params && typeof config.params === 'object') {
|
||||
// 使用URLSearchParams自动处理嵌套对象
|
||||
const searchParams = new URLSearchParams()
|
||||
|
||||
Object.entries(config.params).forEach(([key, value]) => {
|
||||
if (value === undefined || value === null || value === '' || value === 'undefined') {
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
// 对于对象参数,后端期望的是 params[beginTime] 格式
|
||||
Object.entries(value).forEach(([nestedKey, nestedValue]) => {
|
||||
if (nestedValue !== undefined && nestedValue !== null && nestedValue !== '') {
|
||||
searchParams.append(`${key}[${nestedKey}]`, nestedValue.toString())
|
||||
}
|
||||
})
|
||||
} else {
|
||||
searchParams.append(key, value.toString())
|
||||
}
|
||||
})
|
||||
|
||||
const paramsString = searchParams.toString()
|
||||
if (paramsString) {
|
||||
requestUrl += '?' + paramsString
|
||||
}
|
||||
}
|
||||
|
||||
const signature = generateRequestSignature(userId, timestamp, method, requestUrl, userSecret)
|
||||
|
||||
config.headers['timestamp'] = timestamp
|
||||
config.headers['X-Signature'] = signature
|
||||
|
||||
} catch (error) {
|
||||
console.warn('生成防重放签名失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// GET 请求处理 - 统一处理加密逻辑
|
||||
if (config.method === 'get' && config.params) {
|
||||
// 如果需要加密 GET 请求
|
||||
if (systemConfig.requestConfig.encryptRequest && encryptRequest) {
|
||||
// 将参数转换为查询字符串
|
||||
let paramsString = tansParams(config.params)
|
||||
console.error(paramsString);
|
||||
|
||||
// 移除末尾的 & 字符
|
||||
if (paramsString.endsWith('&')) {
|
||||
paramsString = paramsString.slice(0, -1)
|
||||
|
|
|
|||
Loading…
Reference in New Issue