zs 11 mesi fa
parent
commit
5772518d8a
7 ha cambiato i file con 167 aggiunte e 15 eliminazioni
  1. 3 1
      .env.development
  2. 2 1
      .env.production
  3. 44 0
      package-lock.json
  4. 5 0
      package.json
  5. 1 2
      src/layout/parts/Sidebar.vue
  6. 47 11
      src/utils/axios-wrapper.js
  7. 65 0
      src/utils/crypto.js

+ 3 - 1
.env.development

@@ -1,6 +1,8 @@
 ## 开发环境
 NODE_ENV='development'
-
+# 是否启用加密,应与服务配合使用,否则会一端加密,一端不解密
+# VITE必须得写,不写就需要改默认配置.没必要
+VITE_USE_CRYPTO = true
 # 应用端口
 VITE_APP_PORT = 3000
 

+ 2 - 1
.env.production

@@ -3,7 +3,8 @@ NODE_ENV='production'
 
 # 应用端口
 VITE_APP_PORT = 3000
-
+# 是否启用加密,应与服务配合使用,否则会一端加密,一端不解密
+VITE_USE_CRYPTO = true
 # 代理前缀
 VITE_APP_BASE_API = '/ts/frame/api'
 

+ 44 - 0
package-lock.json

@@ -14,8 +14,14 @@
         "@wangeditor/editor": "^5.1.23",
         "@wangeditor/editor-for-vue": "5.1.10",
         "axios": "^1.6.7",
+<<<<<<< HEAD
         "echarts": "^5.5.0",
         "element-plus": "^2.5.6",
+=======
+        "crypto-js": "^4.2.0",
+        "element-plus": "^2.5.6",
+        "jsencrypt": "^3.3.2",
+>>>>>>> 5f88fd1667661a41787fe8f1593b52a5337e7d3f
         "lodash-es": "^4.17.21",
         "moment": "^2.30.1",
         "nprogress": "^0.2.0",
@@ -2056,6 +2062,14 @@
         "node": ">= 8"
       }
     },
+<<<<<<< HEAD
+=======
+    "node_modules/crypto-js": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.2.0.tgz",
+      "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
+    },
+>>>>>>> 5f88fd1667661a41787fe8f1593b52a5337e7d3f
     "node_modules/css-select": {
       "version": "4.3.0",
       "resolved": "https://registry.npmmirror.com/css-select/-/css-select-4.3.0.tgz",
@@ -2333,6 +2347,7 @@
         "domelementtype": "1"
       }
     },
+<<<<<<< HEAD
     "node_modules/echarts": {
       "version": "5.5.0",
       "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.5.0.tgz",
@@ -2347,6 +2362,8 @@
       "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
       "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
     },
+=======
+>>>>>>> 5f88fd1667661a41787fe8f1593b52a5337e7d3f
     "node_modules/element-plus": {
       "version": "2.5.6",
       "resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.5.6.tgz",
@@ -3699,6 +3716,14 @@
         "js-yaml": "bin/js-yaml.js"
       }
     },
+<<<<<<< HEAD
+=======
+    "node_modules/jsencrypt": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmmirror.com/jsencrypt/-/jsencrypt-3.3.2.tgz",
+      "integrity": "sha512-arQR1R1ESGdAxY7ZheWr12wCaF2yF47v5qpB76TtV64H1pyGudk9Hvw8Y9tb/FiTIaaTRUyaSnm5T/Y53Ghm/A=="
+    },
+>>>>>>> 5f88fd1667661a41787fe8f1593b52a5337e7d3f
     "node_modules/json-buffer": {
       "version": "3.0.1",
       "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz",
@@ -6035,9 +6060,12 @@
       "engines": {
         "node": ">=14"
       },
+<<<<<<< HEAD
       "funding": {
         "url": "https://github.com/sponsors/antfu"
       },
+=======
+>>>>>>> 5f88fd1667661a41787fe8f1593b52a5337e7d3f
       "peerDependencies": {
         "@nuxt/kit": "^3.2.2",
         "@vueuse/core": "*"
@@ -6134,9 +6162,12 @@
       "engines": {
         "node": ">=14"
       },
+<<<<<<< HEAD
       "funding": {
         "url": "https://github.com/sponsors/antfu"
       },
+=======
+>>>>>>> 5f88fd1667661a41787fe8f1593b52a5337e7d3f
       "peerDependencies": {
         "@babel/parser": "^7.15.8",
         "@nuxt/kit": "^3.2.2",
@@ -6547,6 +6578,7 @@
       "engines": {
         "node": ">=10"
       }
+<<<<<<< HEAD
     },
     "node_modules/zrender": {
       "version": "5.5.0",
@@ -6560,6 +6592,8 @@
       "version": "2.3.0",
       "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
       "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
+=======
+>>>>>>> 5f88fd1667661a41787fe8f1593b52a5337e7d3f
     }
   },
   "dependencies": {
@@ -7894,6 +7928,11 @@
         "which": "^2.0.1"
       }
     },
+    "crypto-js": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.2.0.tgz",
+      "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
+    },
     "css-select": {
       "version": "4.3.0",
       "resolved": "https://registry.npmmirror.com/css-select/-/css-select-4.3.0.tgz",
@@ -9173,6 +9212,11 @@
         "argparse": "^2.0.1"
       }
     },
+    "jsencrypt": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmmirror.com/jsencrypt/-/jsencrypt-3.3.2.tgz",
+      "integrity": "sha512-arQR1R1ESGdAxY7ZheWr12wCaF2yF47v5qpB76TtV64H1pyGudk9Hvw8Y9tb/FiTIaaTRUyaSnm5T/Y53Ghm/A=="
+    },
     "json-buffer": {
       "version": "3.0.1",
       "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz",

+ 5 - 0
package.json

@@ -17,8 +17,13 @@
     "@wangeditor/editor": "^5.1.23",
     "@wangeditor/editor-for-vue": "5.1.10",
     "axios": "^1.6.7",
+<<<<<<< HEAD
     "echarts": "^5.5.0",
+=======
+    "crypto-js": "^4.2.0",
+>>>>>>> 5f88fd1667661a41787fe8f1593b52a5337e7d3f
     "element-plus": "^2.5.6",
+    "jsencrypt": "^3.3.2",
     "lodash-es": "^4.17.21",
     "moment": "^2.30.1",
     "nprogress": "^0.2.0",

+ 1 - 2
src/layout/parts/Sidebar.vue

@@ -4,7 +4,7 @@
     <el-row class="sidebar">
       <el-col :span="24" class="main">
         <el-col :span="24" class="first">
-          <img :src="siteInfo.logo_url" class="logo-image" />
+          <img src="/logo.png" class="logo-image" />
           <span class="logo-title">{{ $t('login.title') }}</span>
         </el-col>
         <el-col :span="24" class="second">
@@ -18,7 +18,6 @@
 </template>
 
 <script setup>
-import { siteInfo } from '@/layout/site'
 import { UserStore } from '@/store/user'
 import menuItem from './sidebar/items.vue'
 import { useRoute } from 'vue-router'

+ 47 - 11
src/utils/axios-wrapper.js

@@ -1,16 +1,18 @@
 /* eslint-disable no-console */
 /* eslint-disable no-param-reassign */
 
-import { get, isObject } from 'lodash-es'
+import { get, isObject, pick } from 'lodash-es'
 import Axios from 'axios'
 import { trimData, isNullOrUndefined } from './util-methods'
 import { ErrorCode } from './error-code'
 import router from '@/router'
 import i18n from '@/lang'
+import * as crypto from './crypto'
 let currentRequests = 0
+const { VITE_APP_BASE_API, VITE_USE_CRYPTO } = import.meta.env
 
 export class AxiosWrapper {
-  constructor({ baseUrl = import.meta.env.VITE_APP_BASE_API, unwrap = true } = {}) {
+  constructor({ baseUrl = VITE_APP_BASE_API, unwrap = true } = {}) {
     this.baseUrl = baseUrl
     this.unwrap = unwrap
   }
@@ -71,28 +73,62 @@ export class AxiosWrapper {
     const params = get(options, 'params')
     const url = AxiosWrapper.merge(uri, params)
     currentRequests += 1
-    // Indicator.open({
-    //   spinnerType: 'fading-circle',
-    // });
     try {
       let returnData
       const axios = Axios.create({
         baseURL: this.baseUrl,
         withCredentials: true
       })
-      // if (util.token && util.token !== null) axios.defaults.headers.common.Authorization = util.token;
+      // #region 加密部分
+      // 加密,需要根据env文件判断是否启用加密
+      if (VITE_USE_CRYPTO) {
+        // 生成随机字符串
+        const reqCode = crypto.getRandomString()
+        axios.interceptors.request.use(async (config) => {
+          // 加密真实使用的加密字符串
+          const deReqCode = crypto.pemEncrypt(reqCode)
+          // 加密数据并替换
+          config.transformRequest = (data) => {
+            if (data) {
+              // 加密 加密字符串
+              const strData = JSON.stringify(data)
+              // 加密数据
+              const enCodeData = crypto.encrypt(deReqCode, strData)
+              // 替换数据位置
+              return { data: enCodeData }
+            }
+            return undefined
+          }
+          // 添加请求头,将 加密的真实使用加密字符串附上
+          config.headers['api-token'] = deReqCode
+          return config
+        })
+        axios.interceptors.response.use(
+          (response) => {
+            if (get(response, 'data.data')) {
+              let data = crypto.decrypt(reqCode, get(response, 'data.data'))
+              const dobj = JSON.parse(data || '{}')
+              const others = pick(get(response, 'data'), ['errcode', 'errmsg'])
+              response.data = { ...others, ...dobj }
+            }
+            return response
+          },
+          (error) => Promise.reject(error)
+        )
+      }
+      // #endregion
       const token = localStorage.getItem('token')
-      const apiToken = localStorage.getItem('apiToken')
       if (token) axios.defaults.headers.common['token'] = token
-      if (apiToken) axios.defaults.headers.common['api-token'] = apiToken
-      const res = await axios.request({
+
+      const requestOptions = {
         method: isNullOrUndefined(data) ? 'get' : 'post',
         url,
         data,
         responseType: 'json',
         ...options
-      })
-      const returnRes = res.data
+      }
+      const res = await axios.request(requestOptions)
+      let returnRes = res.data
       const { errcode, errmsg, details } = returnRes
       if (errcode) {
         console.warn(`[${uri}] fail: ${errcode}-${errmsg} ${details}`)

+ 65 - 0
src/utils/crypto.js

@@ -0,0 +1,65 @@
+import JSEncrypt from 'jsencrypt'
+import CryptoJS from 'crypto-js'
+/**
+ * 请求参数加密
+ * @param {string} code 加密字符串
+ * @param {string} data 加密数据
+ */
+export function encrypt(code, data) {
+  const ivStr = code.substring(0, 16)
+  const key = CryptoJS.enc.Utf8.parse(code)
+  const iv = CryptoJS.enc.Utf8.parse(ivStr)
+  const srcs = CryptoJS.enc.Utf8.parse(data)
+  const encrypted = CryptoJS.AES.encrypt(srcs, key, {
+    iv,
+    mode: CryptoJS.mode.CBC,
+    padding: CryptoJS.pad.Pkcs7
+  })
+  return encrypted.toString()
+}
+/**
+ * 解密参数加密
+ * @param {string} code 加密字符串
+ * @param {string} data 解密数据
+ */
+export function decrypt(code, data) {
+  const ivStr = code.substring(0, 16)
+  const key = CryptoJS.enc.Utf8.parse(code)
+  const iv = CryptoJS.enc.Utf8.parse(ivStr)
+  const decrypt = CryptoJS.AES.decrypt(data, key, {
+    iv,
+    mode: CryptoJS.mode.CBC,
+    padding: CryptoJS.pad.Pkcs7
+  })
+  return decrypt.toString(CryptoJS.enc.Utf8)
+}
+/**
+ * RSA非对称加密
+ * @param {string} code 加密内容
+ * @returns {string} decodeStr加密后的内容
+ */
+export function pemEncrypt(code) {
+  const jse = new JSEncrypt()
+  const publicKey = `-----BEGIN PUBLIC KEY-----
+  MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKPquvtF8xW2gkjoOb4e4IFryeL1GuW+
+  PyZi4AnKNU0WnYdNmNDeI74mVh8xj+lI7OW5wEsgfdqXbWT51uuWgf0CAwEAAQ==
+  -----END PUBLIC KEY-----`
+  jse.setPublicKey(publicKey)
+  const data = jse.encrypt(code)
+  if (data) return data
+  return ''
+}
+/**
+ * 生成随机字符串
+ * @param {number} len 需要的长度
+ * @returns {string} 随机字符串
+ */
+export function getRandomString(len = 32) {
+  let result = ''
+  const inOptions = 'abacdefghjklmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ0123456789'
+  for (let i = 0; i < len; i += 1) {
+    result += inOptions.charAt(Math.floor(Math.random() * inOptions.length))
+  }
+
+  return result
+}