API.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. const express = require('express')
  2. const Logger = require('./Logger')
  3. const requestLog = require('./requestLog')
  4. const path = require('path')
  5. const fs = require('fs')
  6. const forge = require('node-forge')
  7. const crypto = require('crypto')
  8. const { BaseStdResponse } = require("../BaseStdResponse")
  9. class API {
  10. constructor() {
  11. this.router = express.Router()
  12. this.namespace = ''
  13. this.path = ''
  14. this.method = 'get'
  15. this.encrypt = true
  16. this.privateKey = fs.readFileSync(path.join(__dirname, '../keys/private_key.pem'), 'utf8')
  17. this.logger = new Logger()
  18. this.requestLog = new requestLog()
  19. }
  20. noEncrypt() {
  21. this.encrypt = false
  22. }
  23. setPath(path) {
  24. this.path = path
  25. }
  26. setMethod(method) {
  27. this.method = method.toLowerCase()
  28. }
  29. getRouter() {
  30. return this.router
  31. }
  32. async onRequest(req, res) {
  33. throw new Error('onRequest方法未实现')
  34. }
  35. /**
  36. * 解密数据:使用 RSA 解密 AES 密钥,再用 AES 解密数据
  37. */
  38. decryptPayload(req, res, next) {
  39. if(!this.encrypt)
  40. return next()
  41. try {
  42. let encryptedKey, encryptedData
  43. if (this.method === 'get') {
  44. encryptedKey = req.query.b
  45. encryptedData = req.query.a
  46. } else {
  47. encryptedKey = req.body.b
  48. encryptedData = req.body.a
  49. }
  50. if (!encryptedKey || !encryptedData)
  51. return res.json({
  52. ...BaseStdResponse.MISSING_PARAMETER,
  53. msg: '参数错误。请刷新页面或更新客户端版本'
  54. })
  55. // 1. 解密 AES 密钥(RSA)
  56. const privateKey = forge.pki.privateKeyFromPem(this.privateKey)
  57. const aesKey = privateKey.decrypt(forge.util.decode64(encryptedKey), 'RSAES-PKCS1-V1_5')
  58. // 2. 解密数据(AES)
  59. const decipher = crypto.createDecipheriv('aes-128-ecb', Buffer.from(aesKey, 'utf8'), null)
  60. decipher.setAutoPadding(true)
  61. let decrypted = decipher.update(encryptedData, 'base64', 'utf8')
  62. decrypted += decipher.final('utf8')
  63. decrypted = JSON.parse(decrypted)
  64. const time = Date.now()
  65. if (Math.abs(time - decrypted.time) > 3 * 60 * 1000)
  66. return res.json({
  67. ...BaseStdResponse.ERR,
  68. msg: '请检查计算机时间是否正确!'
  69. })
  70. decrypted.aesKey = aesKey
  71. if (this.method === 'get')
  72. req.query = decrypted
  73. else
  74. req.body = decrypted
  75. const requestId = req.headers['x-request-id'] || ''
  76. res.setHeader('X-Request-ID', requestId)
  77. next()
  78. } catch (err) {
  79. console.error('解密失败:', err)
  80. return res.json({
  81. ...BaseStdResponse.ERR,
  82. msg: '参数错误'
  83. })
  84. }
  85. }
  86. aesEncrypt(data, req) {
  87. let aesKey
  88. if (this.method === 'get')
  89. aesKey = req.query.aesKey
  90. else
  91. aesKey = req.body.aesKey
  92. const cipher = crypto.createCipheriv('aes-128-ecb', Buffer.from(aesKey, 'utf8'), null)
  93. cipher.setAutoPadding(true)
  94. let encryptedData = cipher.update(JSON.stringify(data), 'utf8', 'base64')
  95. encryptedData += cipher.final('base64')
  96. return encryptedData
  97. }
  98. setupRoute() {
  99. this.router[this.method](this.path, this.decryptPayload.bind(this), async (req, res) => {
  100. // 拦截返回值统一加密
  101. const originalJson = res.json.bind(res)
  102. res.json = (data) => {
  103. if(this.encrypt && data.data) {
  104. data.b = this.aesEncrypt(data.data, req)
  105. delete data.data
  106. }
  107. this.requestLog.insertLog(req, data, this.namespace, this.method, this.path)
  108. return originalJson(data)
  109. }
  110. await this.onRequest(req, res)
  111. })
  112. }
  113. }
  114. module.exports = API