智慧园区前端大屏
shuishen
2024-12-18 9a37c5e1b2190c3f6ec71483923922189091dd52
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
/**
 * 全站http配置
 *
 * axios参数说明
 * isSerialize是否开启form表单提交
 * isToken是否需要token
 */
import axios from 'axios'
import router from '@/router/index'
 
import {
  isURL,
  validatenull
} from '@/utils/validate'
 
import {
  ElMessage
} from 'element-plus'
 
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import { Base64 } from 'js-base64'
import { baseUrl } from '@/config/env'
import crypto from '@/utils/crypto'
 
import { useLogin } from '@/store/login'
 
// 全局未授权错误提示状态,只提示一次
let isErrorShown = false
 
// 全局锁机制相关变量
let isRefreshing = false // 标记当前是否正在刷新token
let refreshTokenPromise = null // 刷新token的Promise,避免重复请求
 
// 超时时间设置为10分钟,部分接口上传比较慢,如固件上传
axios.defaults.timeout = 600000
//返回其他状态码
axios.defaults.validateStatus = function (status) {
  return status >= 200 && status <= 500 // 默认的
}
//跨域请求,允许保存cookie
axios.defaults.withCredentials = true
// NProgress Configuration
NProgress.configure({
  showSpinner: false,
})
//http request拦截
axios.interceptors.request.use(
  config => {
    const store = useLogin()
    //地址为已经配置状态则不添加前缀
    if (!isURL(config.url) && !config.url.startsWith(baseUrl)) {
      config.url = baseUrl + config.url
    }
 
    // start progress bar
    NProgress.start()
    // 初始化错误提示状态
    isErrorShown = false
 
    //安全请求header
    config.headers['Blade-Requested-With'] = 'BladeHttpRequest'
    //headers判断是否需要
    const authorization = config.authorization === false
    if (!authorization) {
      config.headers['Authorization'] = `Basic ${Base64.encode(
        `zhyq:zhyq_secret`
      )}`
    }
 
    //headers判断请求是否携带token
    const meta = config.meta || {}
    const isToken = meta.isToken === false
    //headers传递token是否加密
    const cryptoToken = config.cryptoToken === true
    //判断传递数据是否加密
    const cryptoData = config.cryptoData === true
    const token = store.token
 
    if (token && !isToken) {
      config.headers['Blade-Auth'] = cryptoToken
        ? 'crypto ' + crypto.encryptAES(token, crypto.cryptoKey)
        : 'bearer ' + token
    }
    // 开启报文加密
    if (cryptoData) {
      if (config.params) {
        const data = crypto.encryptAES(JSON.stringify(config.params), crypto.aesKey)
        config.params = { data }
      }
      if (config.data) {
        config.text = true
        config.data = crypto.encryptAES(JSON.stringify(config.data), crypto.aesKey)
      }
    }
    //headers中配置text请求
    if (config.text === true) {
      config.headers['Content-Type'] = 'text/plain'
    }
 
    return config
  },
  error => {
    return Promise.reject(error)
  }
)
//http response拦截
axios.interceptors.response.use(
  res => {
    const store = useLogin()
 
    NProgress.done()
    const status = res.data.error_code || res.data.code || res.status
    const statusWhiteList = []
    const message = res.data.msg || res.data.error_description || '系统错误'
    const config = res.config
    const cryptoData = config.cryptoData === true
    //如果在白名单里则自行catch逻辑处理
    if (statusWhiteList.includes(status)) return Promise.reject(res)
 
    // 如果是401并且没有重试过,尝试刷新token
    if (status === 401 && !config._retry) {
      config._retry = true
 
      // 如果当前已经在刷新token,等待刷新完成
      if (isRefreshing) {
        return refreshTokenPromise.then(() => {
          const meta = config.meta || {}
          const isToken = meta.isToken === false
          const cryptoToken = config.cryptoToken === true
 
          const token = store.token
 
          if (token && !isToken) {
            config.headers['Blade-Auth'] = cryptoToken
              ? 'crypto ' + crypto.encryptAES(token, crypto.cryptoKey)
              : 'bearer ' + token
          }
          return axios(config)
        })
      }
 
      // 开始刷新token
      isRefreshing = true
 
      // 调用RefreshToken action来刷新token
      refreshTokenPromise = store.RefreshToken().then(() => {
        const meta = config.meta || {}
        const isToken = meta.isToken === false
        const cryptoToken = config.cryptoToken === true
        // 获取刷新后的token
        const token = store.token
 
        if (token && !isToken) {
          config.headers['Blade-Auth'] = cryptoToken
            ? 'crypto ' + crypto.encryptAES(token, crypto.cryptoKey)
            : 'bearer ' + token
        }
        return axios(config)
      }).catch(() => {
        isRefreshing = false // 重置刷新标志
        // 首次报错时提示
        if (!isErrorShown) {
          isErrorShown = true
          ElMessage({
            message: '用户令牌过期,请重新登录',
            type: 'error',
          })
        }
        // 清除token信息
        store.removeToken()
        store.removeRefreshToken()
        // 重定向到登录页
        store.LogOut().then(res => {
          router.push({
            path: '/login'
          })
        })
        return Promise.reject(new Error(message))
      })
 
      return refreshTokenPromise
    }
 
    // 如果是401并且已经重试过,直接跳转到登录页面
    if (status === 401 && config._retry) {
      if (!isErrorShown) {
        isErrorShown = true
        ElMessage({
          message: '用户令牌过期,请重新登录',
          type: 'error',
        })
      }
      // 清除token信息
      store.removeToken()
      store.removeRefreshToken()
 
      // 重定向到登录页
      store.LogOut().then(res => {
        router.push({
          path: '/login'
        })
      })
      return Promise.reject(new Error(message))
    }
 
    // 如果请求为oauth2错误码则首次报错时提示
    if (status > 2000 && !validatenull(res.data.error_description)) {
      // 首次报错时提示
      if (!isErrorShown) {
        isErrorShown = true
        ElMessage({
          message: message,
          type: 'error',
        })
      }
      return Promise.reject(new Error(message))
    }
    // 如果请求为非200默认统一处理
    if (status !== 200) {
      if (!isErrorShown) {
        ElMessage({
          message: message,
          type: 'error',
        })
      }
      return Promise.reject(new Error(message))
    }
    // 解析加密报文
    if (cryptoData) {
      res.data = JSON.parse(crypto.decryptAES(res.data, crypto.aesKey))
    }
    return res
  },
  error => {
    NProgress.done()
    return Promise.reject(new Error(error))
  }
)
 
export default axios