API.js 4.0 KB

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