如何在 Node.js/HTML 中实现带身份验证的视频流播放

本文详解如何在前后端分离架构下,安全地流式传输受保护的 mp4 视频:前端通过 credentials 模式携带 cookie 中的认证凭证,后端解析 cookie 验证权限,并正确处理 http range 请求以支持拖拽、暂停等播放功能。

要在浏览器中流畅播放受身份验证保护的视频(如需登录或 Token 授权),仅靠

✅ 前端关键配置:启用 credentials 模式

⚠️ 注意事项:

  • crossOrigin="use-credentials" 要求后端响应必须包含 Access-Control-Allow-Credentials: true
  • 同时,Access-Control-Allow-Origin *不能为 `**,必须指定明确的源(如https://www./link/ab3e363159c8a7f02c774f0d6bc7c922`);
  • 浏览器会自动附带当前域下的 Cookie(包括 HttpOnly 的认证 Cookie),无需手动读取或设置 header。

✅ 后端适配:从 Cookie 提取 Token 并校验

你已有的视频流路由需嵌入认证中间件,但注意:视频请求由 。因此,认证逻辑应优先检查 Cookie:

// 示例中间件(Express)
const authenticateFromCookie = (req, res, next) => {
  const token = req.cookies?.auth_token; // 或根据你的 Cookie 名调整,如 'connect.sid'
  if (!token) return res.status(401).send('Unauthorized');

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    next();
  } catch (err) {
    res.status(403).send('Invalid or expired token');
  }
};

// 在视频路由前使用
router.get('/v1/video/get/:id', authenticateFromCookie, (req, res) => {
  const { id } = req.params;
  if (!id) return res.status(500).send({ message: 'Invalid request' });

  const videoPath = `videos/${id}.mp4`;
  // ✅ 确保路径安全:防止目录遍历(建议用 path.join + path.normalize 校验)
  if (!fs.existsSync(videoPath)) {
    return res.status(404).send('Video not found');
  }

  const videoStat = fs.statSync(videoPath);
  const fileSize = videoStat.size;
  const videoRange = req.headers.range;

  if (videoRange) {
    const parts = videoRange.replace(/bytes=/, "").split("-");
    const start = parseInt(parts[0], 10);
    const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;

    if (start >= fileSize || end >= fileSize || start > end) {
      return res.status(416).send('Requested range not satisfiable');
    }

    const chunkSize = end - start + 1;
    const file = fs.createReadStream(videoPath, { start, end });
    res.writeHead(206, {
      'Content-Range': `bytes ${start}-${end}/${fileSize}`,
      'Accept-Ranges': 'bytes',
      'Content-Length': chunkSize,
      'Content-Type': 'video/mp4',
      'Access-Control-Allow-Credentials': 'true',
      'Access-Control-Allow-Origin': 'https://www./link/ab3e363159c8a7f02c774f0d6bc7c922' // ⚠️ 必须显式声明,不可为 *
    });
    file.pipe(res);
  } else {
    res.writeHead(200, {
      'Content-Length': fileSize,
      'Content-Type': 'video/mp4',
      'Access-Control-Allow-Credentials': 'true',
      'Access-Control-Allow-Origin': 'https://www./link/ab3e363159c8a7f02c774f0d6bc7c922'
    });
    fs.createReadStream(videoPath).pipe(res);
  }
});

? 安全增强建议

  • 路径校验:严格校验 id 参数,避免路径遍历攻击(如 ../../../etc/passwd);推荐使用白名单或 UUID,并将视频文件存于非公开目录。
  • Token 绑定:JWT 可绑定用户 ID、IP 或设备指纹,提升会话安全性。
  • CORS 精确配置:生产环境禁用通配符 *,动态匹配可信源(如 res.header('Access-Control-Allow-Origin', allowedOrigins.includes(origin) ? origin : ''))。
  • 流式错误处理:对 fs.createReadStream 添加 .on('error') 监听,避免崩溃。

✅ 总结

实现带认证的视频流,关键在于三点协同:

  1. 前端
  2. 后端 CORS 响应头明确允许凭据且指定 Origin;
  3. 认证中间件从 req.cookies 读取并校验 Token,而非依赖 Authorization 头。

如此,即可在保障安全的前提下,无缝支持现代浏览器的全部视频控制功能(快进、音量调节、全屏等),无需额外封装 Blob 或 MediaSource API。