本文详解如何在前后端分离架构下,安全地流式传输受保护的 mp4 视频:前端通过 credentials 模式携带 cookie 中的认证凭证,后端解析 cookie 验证权限,并正确处理 http range 请求以支持拖拽、暂停等播放功能。
要在浏览器中流畅播放受身份验证保护的视频(如需登录或 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);
}
});实现带认证的视频流,关键在于三点协同:
如此,即可在保障安全的前提下,无缝支持现代浏览器的全部视频控制功能(快进、音量调节、全屏等),无需额外封装 Blob 或 MediaSource API。