Browse Source

✨ feat: 添加加密功能

Pchen. 9 months ago
parent
commit
568b3911df

+ 2 - 0
apis/Corn/StartAutoLepao.js

@@ -7,6 +7,8 @@ const { BaseStdResponse } = require("../../BaseStdResponse");
 class StartAutoLepao extends API {
     constructor() {
         super();
+
+        this.noEncrypt()
         this.setPath('/Corn/StartAutoLepao');
         this.setMethod('GET');
     }

+ 1 - 0
apis/Lepao/Account/UpdateAccount.js

@@ -11,6 +11,7 @@ class UpdateAccount extends API {
     constructor() {
         super();
 
+        this.noEncrypt()
         this.setPath('/Lepao/UpdateAccount')
         this.setMethod('POST')
     }

+ 1 - 0
apis/Public/EncrypedPwd.js

@@ -6,6 +6,7 @@ class EncrypedPwd extends API {
     constructor() {
         super();
 
+        this.noEncrypt()
         this.setPath('/Public/EncrypedPwd')
         this.setMethod('POST')
     }

+ 1 - 0
apis/Upload/UploadAvatar.js

@@ -45,6 +45,7 @@ class UploadAvatar extends API {
     constructor() {
         super();
 
+        this.noEncrypt()
         this.setMethod("POST");
         this.setPath("/UploadAvatar");
     }

+ 1 - 0
apis/Upload/UploadPicture.js

@@ -43,6 +43,7 @@ class UploadAvatar extends API {
     constructor() {
         super();
 
+        this.noEncrypt()
         this.setMethod("POST");
         this.setPath("/UploadPicture");
     }

+ 28 - 0
keys/private_key.pem

@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDOBgVzlDR6ObzG
+MWUnssgB3YXH9KNLj133OXZ3JnvcLR8pKqexkgAV0MlV57OYnM5c1daFpUaGqncY
+FB6inQlHXMXCYrnOhDEhIvv1hDAmWEntnNhr8symQV3gJNPxLnQtqztCKoqMMwAH
+Up0lk2vg0avdNhTHVSI71MyhdbIC1VABK+TIQhAeinE56HAWfh1pM7//XIy2vq+G
+tDzOtDeo+blpt9whDem5fkjBL7MN8kH0WiSwlmS1Xivr7fpJDteO/TUAHaj6bbPq
+Pvednjn/IsvLp8Yud4yNKIsbTwxBoFHm67ccziDYmCOnzIy0Ufk0b2D7m61ovezC
+sUHyrBS1AgMBAAECggEBAMdiWmpFtxfGwuPIMA5gXmI3wd4G6vYYVKcLLJQZYWtv
+G6Yaito14vE582+44j3DG5AKvybuEbgIR2O9LPzRH4a5yQXbx9iOZIGeOtubx4GE
+ll9oGKc3/ki0ICbpXVgfTf5YpeveAeG3BcMQd9dCufeE02Atk6RpxszCoBtuLQ3B
+JwVvcY1CDq3f+BtFDJxy18akIr66Clqch8F+rZCoS4jtNTYq8TOx05HX7KEEX8i/
+HWtejP0Ei8HYBdKVQLZ9iIVYsmtseBP3AIPefud/osUV+H7gp4072l4Wt+53AY38
+dNEhe18HrHr4iHSe+vawHUxWsELDubG4VrvkNqrFa00CgYEA+r5Mo5YXH6kBEUaB
+R43143nad4MfoPT21X4aWJIrDfEX9dp5QIWlA1zn0rEgMbrrKHSiMR3p4vjwBJ1n
+rxzA1ScXQLe2ufv/ElAsi6PRGpGmu+CYasrXkl5Nc1GkBI3gBOMR4G+pxPhxyEoE
+KorbSZslHarfTW8HUfJ7fTb1agcCgYEA0le3ruYV83NFCGpWwm0MIWeV2pIBo9cY
+va/TaHaNuSE+3Bk4NXiMZtlwgZxYTMvAteXqt9ePAKq5Xdq5UJpWGA+WhYHQeMUc
+5BVqhKdNSP+O21F3qx84gVdxs7Qu7VEvgj4vkynqM80L0A3YydrbaTNTeKYTGal0
+w2zepszmTGMCgYEA83diDRlIfKpqae7Oyr7R+b2w3ojIZk5VejlGtae//HqULFml
+kHv0HQ7R3me0ffUkLxUJA64rEwNqcuCv831008NwNZvs5iwEoTCOAEzhVe4FOro6
+5L7ukKXWiFnGa9Giqrwc+JMUAjjGqpcL8o7/nDnz0RnAV3yuW+iZ9ZwusXMCgYB9
+Z+DSfMk/kUzqHIRHfL5LuDdmzb/+j8JnbUmssWoDoyeYD80sTAfhVeg2ziDzAFOP
+31kWbIUMz7yekUYFIU8NH1YNmPzS1CpKbd5I4no4eG0SozqmooGM07atflKwGMQr
+canpoobIhYpya58BkeNYkEpG1zTyCs7bqLACYI05owKBgQDsoFeu55Xz805Rut7E
+sAKGdI/KZEpqCKwWNePZ4xgm7RNCEh+GEzl1l70X/nv06l7/NKUDJ/zUGWKzJtta
+8JUDNiqXNWWXgyuNUJBiXScz63Ns125ogzQTeVkONDmXvkL13/YWSEg4r2wdRUKa
+OkrCk0ekrsdWHf99V7LEWlm0cA==
+-----END PRIVATE KEY-----

+ 9 - 0
keys/public_key.pem

@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzgYFc5Q0ejm8xjFlJ7LI
+Ad2Fx/SjS49d9zl2dyZ73C0fKSqnsZIAFdDJVeezmJzOXNXWhaVGhqp3GBQeop0J
+R1zFwmK5zoQxISL79YQwJlhJ7ZzYa/LMpkFd4CTT8S50Las7QiqKjDMAB1KdJZNr
+4NGr3TYUx1UiO9TMoXWyAtVQASvkyEIQHopxOehwFn4daTO//1yMtr6vhrQ8zrQ3
+qPm5abfcIQ3puX5IwS+zDfJB9FoksJZktV4r6+36SQ7Xjv01AB2o+m2z6j73nZ45
+/yLLy6fGLneMjSiLG08MQaBR5uu3HM4g2Jgjp8yMtFH5NG9g+5utaL3swrFB8qwU
+tQIDAQAB
+-----END PUBLIC KEY-----

+ 112 - 16
lib/API.js

@@ -1,38 +1,134 @@
-const express = require('express');
-const Logger = require('./Logger');
-const path = require('path');
+const express = require('express')
+const Logger = require('./Logger')
+const path = require('path')
+const fs = require('fs')
+const forge = require('node-forge')
+const crypto = require('crypto')
+const { BaseStdResponse } = require("../BaseStdResponse")
 
 class API {
     constructor() {
-        this.router = express.Router();
-        this.namespace = '';
-        this.path = '';
-        this.method = 'get';
+        this.router = express.Router()
+        this.namespace = ''
+        this.path = ''
+        this.method = 'get'
+        this.encrypt = true
 
-        this.logger = new Logger(path.join(__dirname, '../logs/API.log'), 'INFO');
+        this.privateKey = fs.readFileSync(path.join(__dirname, '../keys/private_key.pem'), 'utf8')
+
+        this.logger = new Logger(path.join(__dirname, '../logs/API.log'), 'INFO')
+    }
+
+    noEncrypt() {
+        this.encrypt = false
     }
 
     setPath(path) {
-        this.path = path;
+        this.path = path
     }
 
     setMethod(method) {
-        this.method = method.toLowerCase();
+        this.method = method.toLowerCase()
     }
 
     getRouter() {
-        return this.router;
+        return this.router
     }
 
     async onRequest(req, res) {
-        throw new Error('onRequest方法未实现');
+        throw new Error('onRequest方法未实现')
+    }
+
+    /**
+     * 解密数据:使用 RSA 解密 AES 密钥,再用 AES 解密数据
+     */
+    decryptPayload(req, res, next) {
+        if(!this.encrypt)
+            return next()
+        try {
+            let encryptedKey, encryptedData
+            if (this.method === 'get') {
+                encryptedKey = req.query.encryptedKey
+                encryptedData = req.query.encryptedData
+            } else {
+                encryptedKey = req.body.encryptedKey
+                encryptedData = req.body.encryptedData
+            }
+
+            if (!encryptedKey || !encryptedData)
+                return res.json({
+                    ...BaseStdResponse.MISSING_PARAMETER,
+                    msg: '参数错误。请刷新页面或更新客户端版本'
+                })
+
+            // 1. 解密 AES 密钥(RSA)
+            const privateKey = forge.pki.privateKeyFromPem(this.privateKey)
+            const aesKey = privateKey.decrypt(forge.util.decode64(encryptedKey), 'RSAES-PKCS1-V1_5')
+
+            // 2. 解密数据(AES)
+            const decipher = crypto.createDecipheriv('aes-128-ecb', Buffer.from(aesKey, 'utf8'), null)
+            decipher.setAutoPadding(true)
+            let decrypted = decipher.update(encryptedData, 'base64', 'utf8')
+            decrypted += decipher.final('utf8')
+            decrypted = JSON.parse(decrypted)
+
+            const time = Date.now()
+            if (Math.abs(time - decrypted.time) > 10 * 60 * 1000)
+                return res.json({
+                    ...BaseStdResponse.ERR,
+                    msg: '请检查计算机时间是否正确!'
+                })
+
+            decrypted.aesKey = aesKey
+
+            if (this.method === 'get')
+                req.query = decrypted
+            else
+                req.body = decrypted
+
+            const requestId = req.headers['x-request-id'] || ''
+            res.setHeader('X-Request-ID', requestId)
+
+            next()
+        } catch (err) {
+            console.error('解密失败:', err)
+            return res.json({
+                ...BaseStdResponse.ERR,
+                msg: '参数错误'
+            })
+        }
+    }
+
+    aesEncrypt(data, req) {
+        let aesKey
+        if (this.method === 'get')
+            aesKey = req.query.aesKey
+        else
+            aesKey = req.body.aesKey
+
+        const cipher = crypto.createCipheriv('aes-128-ecb', Buffer.from(aesKey, 'utf8'), null)
+        cipher.setAutoPadding(true)
+        let encryptedData = cipher.update(JSON.stringify(data), 'utf8', 'base64')
+        encryptedData += cipher.final('base64')
+        return encryptedData
     }
 
     setupRoute() {
-        this.router[this.method](this.path, async (req, res) => {
-            await this.onRequest(req, res);
-        });
+        this.router[this.method](this.path, this.decryptPayload.bind(this), async (req, res) => {
+            // 拦截返回值统一加密
+            const originalJson = res.json.bind(res)
+            res.json = (data) => {
+                if(this.encrypt && data.data) {
+                    data.encryptedData = this.aesEncrypt(data.data, req)
+                    delete data.data
+                }
+
+                return originalJson(data)
+            }
+
+            await this.onRequest(req, res)
+        })
     }
 }
 
-module.exports = API;
+module.exports = API

+ 1 - 0
package.json

@@ -17,6 +17,7 @@
     "express": "^4.21.1",
     "multer": "^1.4.5-lts.1",
     "mysql2": "^3.11.0",
+    "node-forge": "^1.3.1",
     "nodemailer": "^6.9.14",
     "puppeteer": "^23.9.0",
     "redis": "^4.7.0",