enc-framework
Version:
enc-framework 核心组件.
354 lines (330 loc) • 13.6 kB
JavaScript
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;