const express = require('express') const Logger = require('./Logger') const requestLog = require('./requestLog') 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.encrypt = true this.privateKey = fs.readFileSync(path.join(__dirname, '../keys/private_key.pem'), 'utf8') this.logger = new Logger() this.requestLog = new requestLog() } noEncrypt() { this.encrypt = false } setPath(path) { this.path = path } setMethod(method) { this.method = method.toLowerCase() } getRouter() { return this.router } async onRequest(req, res) { 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.b encryptedData = req.query.a } else { encryptedKey = req.body.b encryptedData = req.body.a } 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) > 3 * 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, this.decryptPayload.bind(this), async (req, res) => { // 拦截返回值统一加密 const originalJson = res.json.bind(res) res.json = (data) => { this.requestLog.insertLog(req, JSON.parse(JSON.stringify(data)), this.namespace, this.method, this.path) if(this.encrypt && data.data) { data.b = this.aesEncrypt(data.data, req) delete data.data } return originalJson(data) } await this.onRequest(req, res) }) } } module.exports = API