Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,11 @@ RuoYi-Vue-FastAPI是一套全部开源的快速开发平台,毫无保留给个
12. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。
13. 服务监控:监视当前系统CPU、内存、磁盘、堆栈等相关信息。
14. 缓存监控:对系统的缓存信息查询,命令统计等。
15. 在线构建器:拖动表单元素生成相应的HTML代码。
16. 系统接口:根据业务代码自动生成相关的api接口文档。
17. 代码生成:配置数据库表信息一键生成前后端代码(python、sql、vue、js),支持下载。
18. AI管理:提供AI模型管理和AI对话功能。
15. 传输加密:支持前后端请求加密、响应解密、公钥轮换、运行策略下发与监控统计。
16. 在线构建器:拖动表单元素生成相应的HTML代码。
17. 系统接口:根据业务代码自动生成相关的api接口文档。
18. 代码生成:配置数据库表信息一键生成前后端代码(python、sql、vue、js),支持下载。
19. AI管理:提供AI模型管理和AI对话功能。

## 演示图

Expand Down Expand Up @@ -183,6 +184,10 @@ RuoYi-Vue-FastAPI是一套全部开源的快速开发平台,毫无保留给个

## 项目开发及发布相关

### 传输层加解密配置说明

后端密钥配置与轮换说明:[ruoyi-fastapi-backend/docs/transport_crypto_config.md](./ruoyi-fastapi-backend/docs/transport_crypto_config.md)

### 开发

```bash
Expand Down
1 change: 1 addition & 0 deletions ruoyi-fastapi-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"@weapp-tailwindcss/merge-v3": "^0.1.5",
"core-js": "^3.42.0",
"flyio": "^0.6.2",
"node-forge": "^1.4.0",
"regenerator-runtime": "^0.14.1",
"vue": "~2.6.14",
"vuex": "^3.2.0"
Expand Down
215 changes: 138 additions & 77 deletions ruoyi-fastapi-app/src/utils/request.js
Original file line number Diff line number Diff line change
@@ -1,77 +1,138 @@
import store from "@/store";
import config from "@/config";
import { getToken } from "@/utils/auth";
import errorCode from "@/utils/errorCode";
import { toast, showConfirm, tansParams } from "@/utils/common";

let timeout = 10000;
const baseUrl = config.baseUrl;

const request = (config) => {
// 是否需要设置 token
const isToken = (config.headers || {}).isToken === false;
config.header = config.header || {};
if (getToken() && !isToken) {
config.header["Authorization"] = "Bearer " + getToken();
}
// get请求映射params参数
if (config.params) {
let url = config.url + "?" + tansParams(config.params);
url = url.slice(0, -1);
config.url = url;
}
return new Promise((resolve, reject) => {
uni
.request({
method: config.method || "get",
timeout: config.timeout || timeout,
url: config.baseUrl || baseUrl + config.url,
data: config.data,
header: config.header,
dataType: "json",
})
.then((response) => {
let [error, res] = response;
if (error) {
toast("后端接口连接异常");
reject("后端接口连接异常");
return;
}
const code = res.data.code || 200;
const msg = errorCode[code] || res.data.msg || errorCode["default"];
if (code === 401) {
showConfirm(
"登录状态已过期,您可以继续留在该页面,或者重新登录?",
).then((res) => {
if (res.confirm) {
store.dispatch("LogOut").then((res) => {
uni.reLaunch({ url: "/pages/login" });
});
}
});
reject("无效的会话,或者会话已过期,请重新登录。");
} else if (code === 500) {
toast(msg);
reject("500");
} else if (code !== 200) {
toast(msg);
reject(code);
}
resolve(res.data);
})
.catch((error) => {
let { message } = error;
if (message === "Network Error") {
message = "后端接口连接异常";
} else if (message.includes("timeout")) {
message = "系统接口请求超时";
} else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.substr(message.length - 3) + "异常";
}
toast(message);
reject(error);
});
});
};

export default request;
import store from "@/store";
import config from "@/config";
import { getToken } from "@/utils/auth";
import errorCode from "@/utils/errorCode";
import { toast, showConfirm, tansParams } from "@/utils/common";
import {
decryptTransportErrorResponse,
decryptTransportResponse,
encryptTransportRequest,
invalidateTransportKeyMeta,
resetTransportRequestConfig,
shouldRetryTransportWithFreshKey,
} from "@/utils/transportCrypto";

let timeout = 10000;
const baseUrl = config.baseUrl;

/**
* 对外暴露的统一请求方法。
*
* @param {Object} config 请求配置
* @returns {Promise<Object>} 业务响应数据
*/
const request = async (config) => {
// 是否需要设置 token
const isToken = (config.headers || {}).isToken === false;
config.header = config.header || {};
config.headers = config.headers || {};
if (getToken() && !isToken) {
config.header["Authorization"] = "Bearer " + getToken();
}

try {
// 在参数拼接前完成传输层加密,避免明文查询串提前写入 URL。
config = await encryptTransportRequest(config);

// get请求映射params参数
if (config.params) {
let url = config.url + "?" + tansParams(config.params);
url = url.slice(0, -1);
config.url = url;
}

return await new Promise((resolve, reject) => {
uni.request({
method: config.method || "get",
timeout: config.timeout || timeout,
url: config.baseUrl || baseUrl + config.url,
data: config.data,
header: config.header,
dataType: "json",
success: async (response) => {
try {
// 命中传输层加密时,先解密成标准业务响应结构。
const res = await decryptTransportResponse(response, config);

// 若当前响应提示密钥失效,则刷新公钥缓存并基于原始请求重试一次。
if (shouldRetryTransportWithFreshKey(res) && !config.__transportRetried) {
invalidateTransportKeyMeta();
config.__transportRetried = true;
config.headers.repeatSubmit = false;
resetTransportRequestConfig(config);
resolve(await request(config));
return;
}

const code = res.data.code || 200;
const msg = errorCode[code] || res.data.msg || errorCode["default"];
if (code === 401) {
showConfirm(
"登录状态已过期,您可以继续留在该页面,或者重新登录?",
).then((res) => {
if (res.confirm) {
store.dispatch("LogOut").then(() => {
uni.reLaunch({ url: "/pages/login" });
});
}
});
const error = new Error("无效的会话,或者会话已过期,请重新登录。");
error.response = res;
reject(error);
} else if (code === 500) {
const error = new Error(msg);
error.response = res;
reject(error);
} else if (code !== 200) {
const error = new Error(msg);
error.response = res;
reject(error);
} else {
resolve(res.data);
}
} catch (error) {
reject(error);
}
},
fail: reject,
});
});
} catch (error) {
error = await decryptTransportErrorResponse(error, config);
if (shouldRetryTransportWithFreshKey(error) && !config.__transportRetried) {
invalidateTransportKeyMeta();
config.__transportRetried = true;
config.headers.repeatSubmit = false;
resetTransportRequestConfig(config);
return request(config);
}

const response = error.response;
const responseStatus = response?.status ?? response?.statusCode;
const responseCode = response?.data?.code;
const responseMsg = response?.data?.msg;
if (responseMsg) {
uni.showToast({
title: responseMsg,
icon: "none",
duration: responseStatus === 429 || responseCode === 429 ? 5000 : 3000,
});
throw error;
}

let { message } = error;
if (message === "Network Error") {
message = "后端接口连接异常";
} else if (message && message.includes("timeout")) {
message = "系统接口请求超时";
} else if (message && message.includes("Request failed with status code")) {
message = "系统接口" + message.substr(message.length - 3) + "异常";
}
if (message) {
toast(message);
}
throw error;
}
};

export default request;
Loading
Loading