
本文详解如何在前后端分离架构下,安全地流式传输受保护的 mp4 视频:前端通过 credentials 模式携带 cookie 中的认证凭证,后端解析 cookie 验证权限,并正确处理 http range 请求以支持拖拽、暂停等播放功能。
要在浏览器中流畅播放受身份验证保护的视频(如需登录或 Token 授权),仅靠
✅ 前端关键配置:启用 credentials 模式
将
⚠️ 注意事项:
- crossOrigin="use-credentials" 要求后端响应必须包含 Access-Control-Allow-Credentials: true;
- 同时,Access-Control-Allow-Origin *不能为 `**,必须指定明确的源(如https://www.php.cn/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.php.cn/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.php.cn/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') 监听,避免崩溃。
✅ 总结
实现带认证的视频流,关键在于三点协同:
- 前端
- 后端 CORS 响应头明确允许凭据且指定 Origin;
- 认证中间件从 req.cookies 读取并校验 Token,而非依赖 Authorization 头。
如此,即可在保障安全的前提下,无缝支持现代浏览器的全部视频控制功能(快进、音量调节、全屏等),无需额外封装 Blob 或 MediaSource API。











