diff --git a/.env.development b/.env.development index 1f3e44a0023ac9b3da1090fb3b0366156a1834aa..5645e54df992f6e59106ea18d23c3c46583d9341 100644 --- a/.env.development +++ b/.env.development @@ -21,6 +21,7 @@ VITE_CLI_BABEL_TRANSPILE_MODULES = true VITE_APP_PORT = 80 +VITE_APP_ENCRYPT_HEADER = 'encrypt-key' # 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换 VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==' # 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换 diff --git a/.env.production b/.env.production index cbaf04613688055fef3095d3d724c45bd4fb21e3..49b101ca9b1f7af42246ba69d004d8c46f66598a 100644 --- a/.env.production +++ b/.env.production @@ -21,6 +21,7 @@ VITE_BUILD_COMPRESS = gzip VITE_APP_PORT = 80 +VITE_APP_ENCRYPT_HEADER = 'encrypt-key' # 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换 VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==' # 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换 diff --git a/.gitignore b/.gitignore index 21eab35e1c19521f9f999d17caecd7b87b5c7a4f..6beb2c7a80eacf9d6d778d28cf3e570f83fe43bf 100644 --- a/.gitignore +++ b/.gitignore @@ -124,4 +124,5 @@ dist .pnp.* package-lock.json -yarn.lock \ No newline at end of file +yarn.lock +pnpm-lock.yaml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..22a84adbf60372eccccabcaf332b2ba57318f745 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM nginx + +MAINTAINER onesummer + +RUN rm /etc/nginx/conf.d/default.conf + +ADD default.conf /etc/nginx/conf.d/ + +COPY dist/ /usr/share/nginx/html/ diff --git a/default.conf b/default.conf new file mode 100644 index 0000000000000000000000000000000000000000..dfa5f131dcdbc7956ed41f0d45568d9be73df2e9 --- /dev/null +++ b/default.conf @@ -0,0 +1,29 @@ +server { + listen 80; + server_name localhost; + charset utf-8; + + location / { + root /usr/share/nginx/html; + try_files $uri $uri/ /index.html; + index index.html index.htm; + } + + location /prod-api/ { + proxy_pass http://172.16.0.151:8080/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $http_host; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root html; + } +} diff --git a/package.json b/package.json index a1fc8f0898e4dcbeb7947cfd65227b334706b02c..4bacb527205bb3df3e350b51e13f9fb1a292e036 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "license": "MIT", "scripts": { "dev": "vite serve --mode development", - "build:prod": "vite build --mode production &&vue-tsc --noEmit", + "build:prod": "vite build --mode production", "build:stage": "vite build --mode staging", "preview": "vite preview" }, @@ -38,16 +38,17 @@ "@element-plus/icons-vue": "2.1.0", "@vueup/vue-quill": "1.2.0", "@vueuse/core": "9.5.0", + "@zeronejs/utils": "^1.4.0", "animate.css": "4.1.1", "await-to-js": "^3.0.0", - "@zeronejs/utils": "^1.4.0", "axios": "^1.6.2", + "crypto-js": "^4.1.1", "echarts": "5.4.0", "element-plus": "2.5.3", + "encryptlong": "^3.1.4", "file-saver": "2.0.5", "fuse.js": "6.6.2", "jsencrypt": "3.3.1", - "crypto-js": "^4.1.1", "nprogress": "0.2.0", "pinia": "^2.1.7", "vue": "^3.3.8", @@ -56,9 +57,9 @@ "vue-router": "4.1.4" }, "devDependencies": { + "@types/crypto-js": "^4.1.1", "@types/file-saver": "^2.0.5", "@types/js-cookie": "^3.0.3", - "@types/crypto-js": "^4.1.1", "@types/node": "^18.14.6", "@types/nprogress": "^0.2.0", "@typescript-eslint/eslint-plugin": "^5.56.0", diff --git a/src/api/genKeyPair.js b/src/api/genKeyPair.js new file mode 100644 index 0000000000000000000000000000000000000000..06927108894f5e5cb5f51bf3aa4a44ad30d098fa --- /dev/null +++ b/src/api/genKeyPair.js @@ -0,0 +1,14 @@ +import request from '@/utils/request' +export function genKeyPair(callback) { + return request({ + url: '/genKeyPair', + headers: { + isToken: false, + repeatSubmit: false + }, + method: 'get' + }).then((res) => { + return callback(res.data.uuidPrivateKey, res.data.RSA_PUBLIC_KEY) + }) + +} diff --git a/src/api/login.js b/src/api/login.js index 1ff985b6e36d71fb41d15793610eb352a872829e..f3668c9119655b52716d4278c76cc10ad755485a 100644 --- a/src/api/login.js +++ b/src/api/login.js @@ -1,10 +1,11 @@ import request from '@/utils/request' - +import {genKeyPair} from '@/api/genKeyPair' // pc端固定客户端授权id const clientId = import.meta.env.VITE_APP_CLIENT_ID; +const encryptHeader = import.meta.env.VITE_APP_ENCRYPT_HEADER; // 登录方法 -export function login(tenantId,username, password, code, uuid) { +export function login(tenantId, username, password, code, uuid) { const params = { tenantId, username, @@ -14,16 +15,26 @@ export function login(tenantId,username, password, code, uuid) { clientId: clientId, grantType: 'password' } - return request({ - url: '/auth/login', - headers: { - isToken: false, - isEncrypt: true, - repeatSubmit: false - }, - method: 'post', - data: params - }) + return new Promise((resolve, reject) => { + genKeyPair((uuid, public_key) => { + request({ + url: '/auth/login', + headers: { + isToken: false, + isEncrypt: true, + [encryptHeader]: uuid, + publicKey: public_key, + repeatSubmit: false + }, + method: 'post', + data: params + }).then(res => { + resolve(res) + }).catch(error => { + reject(error) + }) + }) + }); } // 注册方法 @@ -33,15 +44,27 @@ export function register(data) { clientId: clientId, grantType: 'password' }; - return request({ - url: '/auth/register', - headers: { - isToken: false, - isEncrypt: true - }, - method: 'post', - data: params + return + new Promise((resolve, reject) => { + genKeyPair((uuid, public_key) => { + request({ + url: '/auth/register', + headers: { + isToken: false, + isEncrypt: true, + [encryptHeader]: uuid, + publicKey: public_key + }, + method: 'post', + data: params + }).then(res => { + resolve(res) + }).catch(error => { + reject(error) + }) + }) }) + } // 获取用户详细信息 diff --git a/src/api/system/tenant.js b/src/api/system/tenant.js index aa35e49a8905dbd99adfc25e7fbd3fa95d0c3569..faba7bb9a93814ab64ca61531b8c31f0600bc54d 100644 --- a/src/api/system/tenant.js +++ b/src/api/system/tenant.js @@ -1,4 +1,7 @@ import request from '@/utils/request'; +import {genKeyPair} from "@/api/genKeyPair"; + +const encryptHeader = import.meta.env.VITE_APP_ENCRYPT_HEADER; // 查询租户列表 export function listTenant(query) { @@ -10,7 +13,7 @@ export function listTenant(query) { } // 查询租户详细 -export function getTenant(tenantId){ +export function getTenant(tenantId) { return request({ url: '/system/tenant/' + tenantId, method: 'get' @@ -19,13 +22,23 @@ export function getTenant(tenantId){ // 新增租户 export function addTenant(data) { - return request({ - url: '/system/tenant', - method: 'post', - headers: { - isEncrypt: true - }, - data: data + return new Promise((resolve, reject) => { + genKeyPair((uuid,public_key)=>{ + request({ + url: '/system/tenant', + method: 'post', + headers: { + isEncrypt: true, + [encryptHeader]: uuid, + publicKey: public_key, + }, + data: data + }).then(res => { + resolve(res) + }).catch(error => { + reject(error) + }) + }) }); } @@ -42,7 +55,7 @@ export function updateTenant(data) { export function changeTenantStatus(tenantId, version, status) { const data = { tenantId, - version, + version, status }; return request({ diff --git a/src/api/system/user.js b/src/api/system/user.js index 646fc50b5dfa825bb0d1b60457b7507efbefa279..ca3be0111e20cd36a111e41de1cc0426e85b082c 100644 --- a/src/api/system/user.js +++ b/src/api/system/user.js @@ -1,6 +1,7 @@ import request from '@/utils/request' import { parseStrEmpty } from "@/utils/ruoyi"; - +import {genKeyPair} from "@/api/genKeyPair"; +const encryptHeader = import.meta.env.VITE_APP_ENCRYPT_HEADER; // 查询用户列表 export function listUser(query) { return request({ @@ -51,13 +52,23 @@ export function resetUserPwd(userId, version, password) { version, password } - return request({ - url: '/system/user/resetPwd', - method: 'put', - headers: { - isEncrypt: true - }, - data: data + return new Promise((resolve, reject) => { + genKeyPair((uuid,public_key)=>{ + request({ + url: '/system/user/resetPwd', + method: 'put', + headers: { + isEncrypt: true, + [encryptHeader]: uuid, + publicKey: public_key, + }, + data: data + }).then(res => { + resolve(res) + }).catch(error => { + reject(error) + }) + }) }) } @@ -98,14 +109,24 @@ export function updateUserPwd(oldPassword, newPassword) { oldPassword, newPassword } - return request({ - url: '/system/user/profile/updatePwd', - method: 'put', - headers: { - isEncrypt: true - }, - data: data - }) + return new Promise((resolve, reject) => { + genKeyPair((uuid,public_key)=>{ + request({ + url: '/system/user/profile/updatePwd', + method: 'put', + headers: { + isEncrypt: true, + [encryptHeader]: uuid, + publicKey: public_key, + }, + data: data + }).then(res => { + resolve(res) + }).catch(error => { + reject(error) + }) + }) + }); } // 用户头像上传 diff --git a/src/utils/jsencrypt.js b/src/utils/jsencrypt.js index 9f69971a24c76f97584af341a8f20cb2a2bec4a7..408bf6db78307089b45bb244f7209eae6463b8de 100644 --- a/src/utils/jsencrypt.js +++ b/src/utils/jsencrypt.js @@ -1,16 +1,15 @@ -import JSEncrypt from 'jsencrypt'; +import JSEncrypt from 'encryptlong'; // 密钥对生成 http://web.chacuo.net/netrsakeypair -const publicKey = import.meta.env.VITE_APP_RSA_PUBLIC_KEY; // 前端不建议存放私钥 不建议解密数据 因为都是透明的意义不大 const privateKey = import.meta.env.VITE_APP_RSA_PRIVATE_KEY; // 加密 -export const encrypt = (txt ) => { +export const encrypt = (txt, publicKey) => { const encryptor = new JSEncrypt(); encryptor.setPublicKey(publicKey); // 设置公钥 - return encryptor.encrypt(txt); // 对数据进行加密 + return encryptor.encryptLong(txt); // 对数据进行加密 }; // 解密 diff --git a/src/utils/request.js b/src/utils/request.js index 44fb268ad1480a8cca14d6ff7b8ad466b7c5a2f2..6704334a73efd985ff0ad5d338e90ffe3cb6cd32 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -74,10 +74,11 @@ service.interceptors.request.use(config => { } // 当开启参数加密 if (isEncrypt && (config.method === 'post' || config.method === 'put')) { - // 生成一个 AES 密钥 - const aesKey = generateAesKey(); - config.headers[encryptHeader] = encrypt(encryptBase64(aesKey)); - config.data = typeof config.data === 'object' ? encryptWithAes(JSON.stringify(config.data), aesKey) : encryptWithAes(config.data, aesKey); + // 根据 AES 密钥进行加密 + const publicKey = (config.headers || {}).publicKey + console.log('publicKey', publicKey) + config.data = typeof config.data === 'object' ? encrypt(encodeURIComponent(JSON.stringify(config.data)), publicKey) : encrypt(encodeURIComponent(config.data), publicKey); + delete config.headers['publicKey']; } // FormData数据去请求头Content-Type if (config.data instanceof FormData) { diff --git a/src/views/login.vue b/src/views/login.vue index f4b6e43d0d4a90b527fee40c481259a7e4625dcb..16d07014da4300c7a95756e9d5e26b50018c0037 100644 --- a/src/views/login.vue +++ b/src/views/login.vue @@ -181,8 +181,10 @@ function handleLogin() { } // 调用action的登录方法 userStore.login(loginForm.value).then(() => { + router.push({path: redirect.value || "/"}); - }).catch(() => { + }).catch((e) => { + console.log(e) loading.value = false; // 重新获取验证码 if (captchaEnabled.value) { @@ -199,9 +201,10 @@ function handleLogin() { async function initTenantList() { const {data} = await getTenantList(); tenantList.value = data.voList; - if (tenantList.value != null && tenantList.value.length !== 0) { - loginForm.value.tenantId = tenantList.value[0].tenantId; - } + // if (tenantList.value != null && tenantList.value.length !== 0) { + // loginForm.value.tenantId = tenantList.value[0].tenantId; + // } + } //检测租户选择框的变化 diff --git a/src/views/system/notice/index.vue b/src/views/system/notice/index.vue index 7485dbae17a519aca35a45a5c757ba27012ab80b..57312cc14d954382cdd65f2c3e20802026829258 100644 --- a/src/views/system/notice/index.vue +++ b/src/views/system/notice/index.vue @@ -11,7 +11,7 @@ - +