From 6b1ff4e408e77aea984d8683d935b98a1c2fef0a Mon Sep 17 00:00:00 2001 From: ykxiao Date: Wed, 9 Jul 2025 10:15:07 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0JWT=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.js | 2 + config/index.js | 3 ++ package-lock.json | 126 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 + routes/stream.js | 31 ++++++++++-- 5 files changed, 161 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index 36c9816..91a9fdb 100644 --- a/app.js +++ b/app.js @@ -6,6 +6,8 @@ * @description Express服务器配置和路由初始化 */ +require('dotenv').config(); + const express = require('express'); const bodyParser = require('body-parser'); const security = require('./middlewares/security'); diff --git a/config/index.js b/config/index.js index 8816379..7ab9d25 100644 --- a/config/index.js +++ b/config/index.js @@ -26,5 +26,8 @@ module.exports = { reconnectStrategy: (retries) => Math.min(retries * 100, 5000) }, ttl: 86400 + }, + jwt: { + jwtSecret: process.env.JWT_SECRET || 'jwt_secret', } }; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 4999608..b5c62ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,10 +10,12 @@ "dependencies": { "body-parser": "^2.2.0", "cors": "^2.8.5", + "dotenv": "^17.1.0", "express": "^5.1.0", "express-rate-limit": "^7.5.1", "express-sse": "^1.0.0", "helmet": "^8.1.0", + "jsonwebtoken": "^9.0.2", "moment-timezone": "^0.6.0", "redis": "^5.5.6", "uuid": "^11.1.0" @@ -112,6 +114,12 @@ "node": ">=18" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -237,6 +245,18 @@ "node": ">= 0.8" } }, + "node_modules/dotenv": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.1.0.tgz", + "integrity": "sha512-tG9VUTJTuju6GcXgbdsOuRhupE8cb4mRgY5JLRCh4MtGoVo3/gfGUtOMwmProM6d0ba2mCFvv+WrpYJV6qgJXQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -251,6 +271,15 @@ "node": ">= 0.4" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -561,6 +590,91 @@ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -818,6 +932,18 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/send": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", diff --git a/package.json b/package.json index ac31843..c8095db 100644 --- a/package.json +++ b/package.json @@ -5,10 +5,12 @@ "dependencies": { "body-parser": "^2.2.0", "cors": "^2.8.5", + "dotenv": "^17.1.0", "express": "^5.1.0", "express-rate-limit": "^7.5.1", "express-sse": "^1.0.0", "helmet": "^8.1.0", + "jsonwebtoken": "^9.0.2", "moment-timezone": "^0.6.0", "redis": "^5.5.6", "uuid": "^11.1.0" diff --git a/routes/stream.js b/routes/stream.js index 17cf9bc..ad5b901 100644 --- a/routes/stream.js +++ b/routes/stream.js @@ -7,15 +7,40 @@ */ const express = require('express'); -const { v4: uuidV4 } = require('uuid'); -const { setupSSEHeaders } = require('../lib/sse'); +const {v4: uuidV4} = require('uuid'); +const {setupSSEHeaders} = require('../lib/sse'); const clients = require('../lib/clients'); const timestamp = require('../utils/timeFormatter'); +const jwt = require('jsonwebtoken'); +const config = require('../config'); // 引入配置 const router = express.Router(); router.get('/', (req, res) => { - const clientId = req.query.clientId || uuidV4(); + const token = req.query.token; + // 增加解析token逻辑, + if (!token) { + res.status(401).json({message: 'Unauthorized: Token missing'}); + return; + } + // 解析Token 提取客户端ID + let clientId + + try { + // 解析Token并验证签名: + const decoded = jwt.verify(token, config.jwt.jwtSecret, { + algorithms: ['HS256'], + clockTolerance: 15 // 防止 Hyperf 生成后立即验证因 nbf 失败 + }); + + clientId = decoded.claims?.user_client_id || decoded.sub || uuidV4(); + } catch (err) { + res.status(401).json({ + message: 'Unauthorized: Invalid token', + error: err.message + }); + return; + } setupSSEHeaders(res);