API.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  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. const AccessControl = require('./AccessControl')
  10. class API {
  11. constructor() {
  12. this.router = express.Router()
  13. this.namespace = ''
  14. this.path = ''
  15. this.method = 'get'
  16. this.encrypt = true
  17. this.permissionCodes = []
  18. this.privateKey = fs.readFileSync(path.join(__dirname, '../keys/private_key.pem'), 'utf8')
  19. this.logger = new Logger()
  20. this.requestLog = new requestLog()
  21. }
  22. noEncrypt() {
  23. this.encrypt = false
  24. }
  25. setPath(path) {
  26. this.path = path
  27. }
  28. setMethod(method) {
  29. this.method = method.toLowerCase()
  30. }
  31. setPermissionCode(code) {
  32. this.permissionCodes = code ? [code] : []
  33. }
  34. setPermissionCodes(codes) {
  35. this.permissionCodes = Array.isArray(codes) ? codes.filter(Boolean) : []
  36. }
  37. getRouter() {
  38. return this.router
  39. }
  40. async onRequest(req, res) {
  41. throw new Error('onRequest方法未实现')
  42. }
  43. /**
  44. * 解密数据:使用 RSA 解密 AES 密钥,再用 AES 解密数据
  45. */
  46. decryptPayload(req, res, next) {
  47. if(!this.encrypt)
  48. return next()
  49. try {
  50. let encryptedKey, encryptedData
  51. if (this.method === 'get') {
  52. encryptedKey = req.query.b
  53. encryptedData = req.query.a
  54. } else {
  55. encryptedKey = req.body.b
  56. encryptedData = req.body.a
  57. }
  58. if (!encryptedKey || !encryptedData)
  59. return res.json({
  60. ...BaseStdResponse.MISSING_PARAMETER,
  61. msg: '参数错误。请刷新页面或更新客户端版本'
  62. })
  63. // 1. 解密 AES 密钥(RSA)
  64. const privateKey = forge.pki.privateKeyFromPem(this.privateKey)
  65. const aesKey = privateKey.decrypt(forge.util.decode64(encryptedKey), 'RSAES-PKCS1-V1_5')
  66. // 2. 解密数据(AES)
  67. const decipher = crypto.createDecipheriv('aes-128-ecb', Buffer.from(aesKey, 'utf8'), null)
  68. decipher.setAutoPadding(true)
  69. let decrypted = decipher.update(encryptedData, 'base64', 'utf8')
  70. decrypted += decipher.final('utf8')
  71. decrypted = JSON.parse(decrypted)
  72. const time = Date.now()
  73. if (Math.abs(time - decrypted.time) > 3 * 60 * 1000)
  74. return res.json({
  75. ...BaseStdResponse.ERR,
  76. msg: '请检查计算机时间是否正确!'
  77. })
  78. decrypted.aesKey = aesKey
  79. if (this.method === 'get')
  80. req.query = decrypted
  81. else
  82. req.body = decrypted
  83. const requestId = req.headers['x-request-id'] || ''
  84. res.setHeader('X-Request-ID', requestId)
  85. next()
  86. } catch (err) {
  87. console.error('解密失败:', err)
  88. return res.json({
  89. ...BaseStdResponse.ERR,
  90. msg: '参数错误'
  91. })
  92. }
  93. }
  94. aesEncrypt(data, req) {
  95. let aesKey
  96. if (this.method === 'get')
  97. aesKey = req.query.aesKey
  98. else
  99. aesKey = req.body.aesKey
  100. const cipher = crypto.createCipheriv('aes-128-ecb', Buffer.from(aesKey, 'utf8'), null)
  101. cipher.setAutoPadding(true)
  102. let encryptedData = cipher.update(JSON.stringify(data), 'utf8', 'base64')
  103. encryptedData += cipher.final('base64')
  104. return encryptedData
  105. }
  106. async checkPermission(req, res, next) {
  107. try {
  108. const payload = this.method === 'get' ? req.query : req.body
  109. const requiredCodes = this.permissionCodes.length > 0
  110. ? this.permissionCodes
  111. : await AccessControl.getResourceRequiredCodes({
  112. method: this.method,
  113. path: this.path
  114. })
  115. if (!requiredCodes || requiredCodes.length === 0)
  116. return next()
  117. const { uuid, session } = payload || {}
  118. if ([uuid, session].some(value => value === '' || value === null || value === undefined))
  119. return res.json({ ...BaseStdResponse.MISSING_PARAMETER })
  120. if (!await AccessControl.checkSession(uuid, session))
  121. return res.status(401).json({ ...BaseStdResponse.ACCESS_DENIED })
  122. if (!await AccessControl.canAccess(uuid, requiredCodes))
  123. return res.json({ ...BaseStdResponse.PERMISSION_DENIED })
  124. return next()
  125. } catch (err) {
  126. this.logger.error(`权限校验失败: ${err.stack || err}`)
  127. return res.json({ ...BaseStdResponse.ERR, msg: '权限校验失败,请稍后再试' })
  128. }
  129. }
  130. setupRoute() {
  131. this.router[this.method](this.path, this.decryptPayload.bind(this), this.checkPermission.bind(this), async (req, res) => {
  132. // 拦截返回值统一加密
  133. const originalJson = res.json.bind(res)
  134. res.json = (data) => {
  135. this.requestLog.insertLog(req, JSON.parse(JSON.stringify(data)), this.namespace, this.method, this.path)
  136. if(this.encrypt && data.data) {
  137. data.b = this.aesEncrypt(data.data, req)
  138. delete data.data
  139. }
  140. return originalJson(data)
  141. }
  142. await this.onRequest(req, res)
  143. })
  144. }
  145. }
  146. module.exports = API