一、axios请求器和响应拦截器的封装
// 这个是全局有4个验证方式 一个 otp验证、一个登录MFA验证一个是提现MFA验证一个是二级密码验证。 // 这个待补充的有: 当响应体状态码为 200 的时候返回的统一的消息提示和数据的处理。 当响应体的状态码不是200的时候, 返回统一的消息提示和数据的处理。 import axios from 'axios' import Vue from 'vue' import { Encrypt, Decrypt } from '../utils/secret.js' //传参 接参 使用加解密 import router from '@/router/index.js' import sha256 from 'crypto-js/sha256' // 配置axios默认设置 axios.defaults.withCredentials = true // 环境判断 const isLocal = window.location.hostname === 'localhost' const BASE_URL = isLocal ? 'http://192.168.2.104:8002/admin' : '/admin' // const BASE_URL = isLocal ? 'https://admin.tokensafe.io:81/admin' : '/admin' // 47.236.8.103 // 创建axios实例 const instance = axios.create({ baseURL: BASE_URL, withCredentials: true }) // 10001 401 100 423 402 429 // 常量定义 错误状态码 const ERROR_CODES = { SUCCESS: 200, REFRESH_REQUIRED: 10001, UNAUTHORIZED: 401, PAYMENT_REQUIRED: 402, FORBIDDEN: 403, LOCKED: 423, RATE_LIMIT: 429, SECOND_PASSWORD_REQUIRED: 1104, // 二级验证 PERMISSION_DENIED: 6006, // otp处理中 NOPERMISSIONS_DENIED: 2003, //权限不足 OTP_DISABLED: 6015, MFA_REQUIRED: 6000 // mfa验证 } // 全局安全验证级别码 const VERIFICATION_TYPES = { OTP: 1, LOGIN_MFA: 2, WITHDRAWAL_MFA: 3, SECOND_LEVEL: 4, NO_VERIFICATION: 5 } // 工具函数 const getVerificationType = () => { // 验证方式为 otp if (localStorage.is_otp === '2') return VERIFICATION_TYPES.OTP // 验证方式为 mfa if (localStorage.is_open_google === '2') return VERIFICATION_TYPES.LOGIN_MFA // 其余为 二级 return VERIFICATION_TYPES.SECOND_LEVEL } const getAuthHeaders = (encryptedUserId, xAppMark) => { // 必传 请求头 测试环境 const baseHeaders = { timezone: 'Asia/Shanghai', 'accept-language': 'zh-CN', 'x-app-mark': xAppMark, 'x-user-id': encryptedUserId } // 本地环境添加额外的认证头: at rt 设备id if (isLocal) { const accessToken = localStorage.getItem('access-token') || '' const refreshToken = localStorage.getItem('rt') || '' const deviceId = localStorage.getItem('deviceid') || '' return { ...baseHeaders, 'access-token': accessToken, 'refresh-token': refreshToken, 'device-id': deviceId } } return baseHeaders } const handleErrorResponse = response => { const { code, message } = response.data switch (code) { case ERROR_CODES.REFRESH_REQUIRED: Vue.prototype.$message.error('请刷新页面') break case ERROR_CODES.UNAUTHORIZED: case ERROR_CODES.FORBIDDEN: Vue.prototype.$message.error(message) clearUserData() router.push('/login') break case ERROR_CODES.LOCKED: case ERROR_CODES.PAYMENT_REQUIRED: if (isLocal) { clearUserData() router.push('/login') } break case ERROR_CODES.SECOND_PASSWORD_REQUIRED: localStorage.setItem('is_open_google', '1') location.reload() break case ERROR_CODES.OTP_DISABLED: Vue.prototype.$message.error('账户已被OTP平台禁用,请联系OTP管理人员') break case ERROR_CODES.NOPERMISSIONS_DENIED: // 2003 权限不足不显示消息 break case ERROR_CODES.PERMISSION_DENIED: // 6006 otp处理中 break case ERROR_CODES.RATE_LIMIT: // 429错误不显示消息,避免频繁提示 break default: if (code !== ERROR_CODES.MFA_REQUIRED) { Vue.prototype.$message.error(message || '请求失败', 1) } } return response } // 清除用户信息 const clearUserData = () => { localStorage.removeItem('access-token') localStorage.removeItem('rt') localStorage.clear() } // 请求拦截器 instance.interceptors.request.use( config => { const userId = localStorage.getItem('user_id') const encryptedUserId = localStorage.getItem('encrypted_user_name') || '' // 生成请求签名 const requestId = `user_id${userId}` const requestBody = JSON.stringify(config.data || {}) const signatureContent = `${requestId}${requestBody}` const xAppMark = sha256(signatureContent).toString() const timestamp = Math.floor(Date.now() / 1000) const verifyTypeStatus = getVerificationType() // 验证方式工具函数 // 加密请求数据 if (config.data) { const encryptedData = Encrypt( JSON.stringify({ ...config.data, timestamp, verify_type_status: verifyTypeStatus }) ) config.data = { data: encryptedData } } // 设置请求头 根据环境区分传递标头 在 getAuthHeaders 已处理 config.headers = { ...config.headers, ...getAuthHeaders(encryptedUserId, xAppMark) } return config }, error => { console.error('请求错误:', error) Vue.prototype.$message.error(error.message || '网络错误') return Promise.reject(error) } ) // 响应拦截器 instance.interceptors.response.use( response => { const { code, data } = response.data if (code === ERROR_CODES.SUCCESS && data) { try { response.data.data = JSON.parse(Decrypt(data)) } catch (error) { console.error('数据解密失败:', error) Vue.prototype.$message.error('数据处理失败') } } else { handleErrorResponse(response) } return response }, error => { console.error('响应错误:', error) if (error.response) { // 服务器返回错误状态码 const { status, data } = error.response if (status === 401 || status === 403) { clearUserData() router.push('/login') } Vue.prototype.$message.error(data?.message || `请求失败: ${status}`) } else if (error.request) { // 请求已发出但没有收到响应 Vue.prototype.$message.error('网络错误,请检查网络连接') } else { // 其他错误 Vue.prototype.$message.error(error.message || '未知错误') } return Promise.reject(error) } ) export default instance