UNPKG

enc-framework

Version:

enc-framework 核心组件.

354 lines (330 loc) 13.6 kB
import axios from "axios"; import md5 from 'js-md5'; import SessionContextHodler from "./ajm-session"; axios.defaults.crossDomain = true; import AjmConfig from '../utils/ajm-config'; import AjmHttpStatus from '../utils/ajm-http-status' // 爱加密http请求对象 function AjmHttp(options){ // 配置项 this.options = options || {}; // 服务基础地址 let baseUrl = this.options.baseUrl || AjmConfig.getApiUrl() || ""; // 文根 let contextPath = this.options.contextPath || ""; this.contextPath = contextPath; // 创建axios实例 let $http = axios.create({ // 基础url baseURL: baseUrl + contextPath, // 请求超时时间 timeout: AjmConfig.get('axiosTimeOut')||30000 }); //越权处理 SATRT /** * @param url 请求的url * @returns {{}} 将url中请求参数组装成json对象(url的?后面的参数) */ function parseQueryString(url) { let urlReg = /^[^\?]+\?([\w\W]+)$/, paramReg = /([^&=]+)=([\w\W]*?)(&|$|#)/g, urlArray = urlReg.exec(url), result = {}; if (urlArray && urlArray[1]) { let paramString = urlArray[1], paramResult; while ((paramResult = paramReg.exec(paramString)) != null) { result[paramResult[1]] = paramResult[2]; } } return result; } /** * 转换请求参数为json对象 * @param {*} config */ function parseRequestParam(config) { let param = {}; if (config.method == "get") { //如果请求类型为get,则解析?后面请求参数,并转化为json对象参数 param = parseQueryString(config.url); } else if (config.method == "post") { param = config.data; //如果post请求类型参数为字符串参数,则转化为json对象参数 if (typeof (param) == 'string') { let reg = /([^&=]+)=([\w\W]*?)(&|$|#)/g; let result = {}, paramResult; while ((paramResult = reg.exec(param)) != null) { result[paramResult[1]] = paramResult[2]; } param = result; } } return param; } function replaceUndefinedOrNull(key, value) { if (value === null || value === undefined) { return undefined; } return value; } function sortObject(src) { var out; if(typeof src === 'object' && Object.keys(src).length > 0) { if(src instanceof Array){ out = [] }else{ out = {} } Object.keys(src).sort().forEach(function(key) { if(src[key] !== null){ out[key] = sortObject(src[key]); } }); return out; } return src; } /** * json参数升序,并转化为json字符串 * @param jsonObj 发送参数 */ function sortAscToString(jsonObj) { let paramStr = ""; if (jsonObj instanceof Array) { let jsonArray = sortObject(jsonObj); paramStr = JSON.stringify(jsonArray, replaceUndefinedOrNull) } else { let arr = new Array(); let num = 0; for (let i in jsonObj) { arr[num] = i; num++; } let sortArr = arr.sort(); //let sortObj = {}; for (let i in sortArr) { let value; if (jsonObj[sortArr[i]] instanceof Date) { value = new Date(jsonObj[sortArr[i]]).getTime(); } else if (typeof jsonObj[sortArr[i]] == "object") { if (jsonObj[sortArr[i]]) { //console.log('aaa==='+jsonObj[sortArr[i]]) value = sortObject(jsonObj[sortArr[i]]); //console.log('bbb==='+value) value = JSON.stringify(value, replaceUndefinedOrNull) } } else { value = jsonObj[sortArr[i]] } if (typeof (value) != "undefined") { paramStr = paramStr + sortArr[i] + value } } } return paramStr; } function handleParamSign(config) { //解析请求参数为json对象 //获取客户端id let clientId = AjmConfig.get("clientId")||"admin"; //获取时间戳 let timestamp = new Date().getTime(); //解析请求参数 let param = parseRequestParam(config); //如果请求参数为空,则对整个请求url做为参数,主要防止对restful api中的参数进行篡改 let paramStr; if (JSON.stringify(param) == "{}") { //处理ios服务器签名问题 let urlStr = config.url.replace(config.baseURL,''); let isIoS = urlStr.indexOf('/ipa/')>=0||urlStr.indexOf('/s2s/')>=0; if(isIoS){ paramStr = config.url.replace(config.baseURL,AjmConfig.get('IOSHOST')) }else{ paramStr = config.url.replace(config.baseURL,AjmConfig.get('SIGNHOST')) } //paramStr = config.url } else { //对json参数进行升序排序,并转化为字符串 paramStr = sortAscToString(param) } //console.log(window.decodeURIComponent(paramStr)) //按照clientId+参数+时间戳+clientId拼接成完整的签名字符串 let signStr = clientId + window.decodeURIComponent(paramStr) + timestamp + clientId // let signStr = clientId + paramStr + timestamp + clientId //将签名字符串进行md5加密,并转化为大写 let sign = md5(signStr).toUpperCase(); //将签名 客户端id 时间戳添加到请求头中 config.headers['sign'] = sign; config.headers['clientid'] = clientId; config.headers['timestamp'] = timestamp; } //越权处理END // 正在进行中的请求列表 const pending = [] const CancelToken = axios.CancelToken const removePending = (config) => { for(let i in pending){ if(pending[i].url === config.url) { //在当前请求在数组中存在时执行取消函数 pending[i].f(); //执行取消操作 // pending.splice(i, 1); 根据具体情况决定是否在这里就把pending去掉 } } } // 是否正在刷新的标记 let isRefreshing = false /*存储请求的数组*/ let refreshSubscribers = [] /*将所有的请求都push到数组中*/ function subscribeTokenRefresh(cb) { refreshSubscribers.push(cb); } /*数组中的请求得到新的token之后自执行,用新的token去请求数据*/ function onRrefreshed(token) { refreshSubscribers.map(cb => cb(token)); } // 初始化 function init () { $http.interceptors.request.use(config => { var url = config.url; if (!url) { console.warn("请求地址没有配置!"); return config; } var uri = config.url.replace(config.baseURL, ""); //判断是否为API开头的接口路径 let isApiUrl = uri.startsWith("/api") || uri.startsWith("api") // 获取context对象 var token = SessionContextHodler.getContext().getToken(); if (isApiUrl && token) { config.headers.Authorization = "Bearer " + token; } //对接口参数进行签名,解决数据篡改漏洞 handleParamSign(config) //获取token接口绕过 if (config.url.indexOf('/oauth/token') >= 0) { // 拦截重复请求(即当前正在进行的相同请求) removePending(config) config.cancelToken = new CancelToken(function executor(c){//本次axios请求的配置添加cancelToken pending.push({ url: config.url, f:c }); }) return config } //每次请求自动更新token有效期 if(window.$tokenMonitor && token){ //token 存活时间 let tokenSurvivalTime = window.$tokenMonitor.startTime //自定义刷新Token时间 let tokenRefshTime = window.$tokenMonitor.refshTime let tokenInfo = SessionContextHodler.getContext().getTokenInfo()||{}; //后端返回token的有效时间 let expiresTime = tokenInfo.expires_in || 0 //获取TOKEN的时间 let getTokenDateStr = tokenInfo.getTokenDateStr //当前时间 let nowDateStar = new Date().getTime() //console.log(expiresTime , tokenSurvivalTime , tokenRefshTime) if(nowDateStar - expiresTime*1000 < getTokenDateStr){ if( expiresTime - tokenSurvivalTime <= tokenRefshTime || tokenSurvivalTime ==0 || tokenSurvivalTime>expiresTime-tokenRefshTime){ // console.log('执行刷新Token') if(!isRefreshing){ isRefreshing = true window.$tokenMonitor.getTokenByRefreshToken((res)=>{ let access_token = res.access_token if (isApiUrl && token) { config.headers.Authorization = "Bearer " + access_token; } isRefreshing = false onRrefreshed(access_token) }).then(()=>{ // console.log(config.url) }) } // 因为config中的token是旧的,所以刷新token后要将新token传进来 const retryOriginalRequest = new Promise((resolve) => { subscribeTokenRefresh((token) => { if (isApiUrl && token) { config.headers.Authorization = "Bearer " + token; } resolve(config) }) }) return retryOriginalRequest } } //window.$tokenMonitor.reSetSurvivalTime() } //config.headers.crossDomain = "true"; return config }, function (error) { // 请求错误方法 return Promise.reject(error); }); // http响应拦截器 $http.interceptors.response.use(data => {// 响应成功关闭loading return data; }, error => { let $vue = window.$vue; var resp = error.response; var message = "系统繁忙,请稍后重试!"; var isLogout = false; if (error.code == "ECONNABORTED") { isLogout = false; message = "服务器断开连接!"; } // 错误数据 message = AjmHttpStatus.getHttpErrorMsg(error); var errorData = error; if (error.response) { switch (error.response.status) { case 401: message = error.response.message||'会话过期或会话信息错误,请重新登录。'; isLogout = true; $vue.$store.dispatch('user/clearUser',$vue.$context.cookiePath); } error.message = message; let config = error.config; let isTips = config.url != config.baseURL+"/oauth/token"; if($vue){ let $message = $vue.$message; if(isTips && !isLogout){ if($message){ $message.closeAll(); $message({ message: message, type: "error", duration: 1500 }); } } let isLogouting = $vue.isLogouting; if(isLogout && ($vue.$route.path != $vue.$context.loginPath && $vue.$route.path != "/") && !isLogouting){ $vue.isLogouting = true; $vue.$alert(message, '系统提示', { confirmButtonText: '确定', callback: action => { $vue.isLogouting = false; $vue.$store.dispatch('user/clearUser',$vue.$context.cookiePath); $vue.$router.push({ path: $vue.$context.loginPath }); } }); } } } return Promise.reject(error); }); } // 设置$http对象 this.$http = $http; // 初始化 http对象 init(); // 返回对象 return this; } // 获取httpClient对象 AjmHttp.prototype.getHttpClient = function(){ return this.$http; }; export default AjmHttp;