Skip to content

视频鉴权:签名 URL 与失效处理

如果你的视频不是公开的,需要控制"谁能看",这一页讲清楚 Sentinel 支持什么、不支持什么, 以及签名过期时会发生什么


一、只有一种鉴权方式:签名 URL

把鉴权信息拼在 URL 上,由 CDN / 源站校验:

https://cdn.example.com/live/room-123.m3u8?token=eyJhbGc...&expires=1752800000&sig=a1b2c3

参数名叫什么、怎么签、有效期多久,完全由你的 CDN 决定,SDK 不关心也不解析—— 它只负责把这个 URL 原样交给浏览器去请求。

不支持自定义请求头

source.headers / source.getHeaders 已被移除(ADR-022)。不要找了,没有。

原因是物理限制,不是我们偷懒:iOS Safari 播 HLS 时,.m3u8.ts 的请求由 系统底层发出,JS 层根本拦不到,没有任何办法给它们加 header(坑 #26)。

如果保留 headers 能力,结果就是"在 Chrome 上测好了,上线后 iPhone 全挂"。 所以我们干脆不提供这个幻觉,强制所有人走签名 URL——这是唯一四端一致的方案。


二、关键认知:HLS 不是一个请求,是几百个

这是最容易翻车的地方。播一个 HLS 视频,浏览器实际发出的请求是:

master.m3u8          ← 主播放列表        1 个
  └─ 720p.m3u8       ← 媒体播放列表      1 个(直播还会持续轮询刷新)
       ├─ seg-001.ts ← 分片              
       ├─ seg-002.ts
       └─ ...        ← 每 2-10 秒一个,一小时的片子几百上千个

每一个都是独立的 HTTP 请求,每一个都要通过 CDN 的鉴权。

所以签名的有效期不是"够打开播放器就行",而是必须覆盖整个观看时长

典型事故

签名 TTL 设 5 分钟,自测时点开就播、一切正常。上线后用户看到第 6 分钟, 分片开始 403,画面卡死。而你的监控里"首帧成功率 100%"。


三、签名过期了会怎样

你会收到一个错误事件

vue
<script setup lang="ts">
import type { PlayerError } from '@sentinel-lab/video-vue-frame'
import { VideoPlayerFrame } from '@sentinel-lab/video-vue-frame'

const src = 'https://your-cdn/video.m3u8'

function onError(e: PlayerError) {
  if (e.code === 'E_AUTH_EXPIRED') {
    // 签名失效了
  }
}
</script>

<template>
  <VideoPlayerFrame :source="src" @error="onError" />
</template>
错误码触发条件retryable
E_AUTH_EXPIRED分片 / 播放列表请求返回 401 或 403false

为什么 403 也算"过期"而不是"无权限"

CDN 上签名过期返回的通常就是 403,和"这个用户本来就没权限"用的同一个状态码, 从 HTTP 层无法区分。所以统一按签名过期处理——这两种情况下业务方要做的事 恰好也一样:去重新取一个 URL。

retryable: false 是什么意思

意思是原样重试没有意义。SDK 不会自动重连,因为拿同一个过期 URL 再请求一百次, 一百次都是 403。

SDK 不会替你刷新签名(ADR-022)。它不知道你的签发接口在哪、要带什么参数、 用户当前的登录态是什么。这件事只能业务层做。


四、怎么恢复

React / Vue(inline 或 iframe)—— 宿主完整换源后重建

业务层拿到新授权后,向自己的服务请求完整的新 MediaSource,再以递增的组件 key 重建播放器。 消费组件不暴露 load();更重要的是,FLV/HLS 内核变化和直播/点播语义变化不能热切,权益变化不应由 前端猜测浏览器最终选中的内核。

宿主维护的事务应当是:

  1. 保存点播位置和用户的播放/暂停意图;直播不保存旧时间点。
  2. 请求服务端按当前权益签发完整新源。多清晰度由新的 HLS Master Playlist 决定,不在前端拼“高清 URL”。
  3. 更新 source 并递增 React key / Vue :key,让新播放器建立新的播放会话。
  4. 新播放器 ready 后,点播恢复有效位置和播放意图;直播回新流 live edge。
  5. 仅当时间或画面真的推进后判定恢复成功;无权、下架和区域限制显示业务 CTA,不重连旧 URL。

用户是有感的

重建会重新缓冲,点播可能短暂闪动;直播会回到新流最新位置。这是安全的授权恢复路径,不是无感热更新。 签名 TTL 仍应覆盖合理播放窗口,以减少发生频率。

用户是有感的

这个过程会重新缓冲。点播会看到一次卡顿和进度条闪动;直播则会直接跳到最新位置。 它是兜底,不是解决方案——真正的解决方案是让签名别在观看途中过期。

静态 iframe URL —— 无法恢复

静态场景没有命令通道,你调不了 load

唯一能做的是在宿主页面监听到 error 后,用新 URL 重建整个 iframe:

js
import { on } from '@sentinel-lab/video-embed-helper'

// ⚠️ 回调必须是 async —— 里面要 await 换签名。这一行此前漏了 `async`,照抄会语法错误
on('error', async (e) => {
  if (e.code === 'E_AUTH_EXPIRED') {
    iframe.src = buildEmbedUrl(await fetchSignedUrl())  // 整个播放器重来
  }
})

静态 iframe URL 没有通用 seek 句柄,不能承诺客户端续播;由服务端针对新源决定从头播还是提供可续播的 播放策略。

结论

需要鉴权的视频,不推荐用静态 iframe URL 接入。 如果非用不可,签名 TTL 必须给得非常宽裕。


五、推荐的签发规则

因为没有一个四端通用的逐分片无感改签方案,同时又要具备宿主完整换源兜底,我们的建议很直接:

1. TTL 要覆盖"最长可能的观看时长",再乘个系数

场景建议 TTL
短视频 / 预览片(< 5 分钟)≥ 30 分钟
长视频点播(电影、课程)≥ 视频时长 × 2,且不低于 6 小时
直播≥ 6 小时,或用下面的方案 2

用户会暂停去吃饭、会切后台、会挂着不关。按"视频多长"算 TTL 是不够的。

直播没有"总时长"可言,靠拉长 TTL 治标不治本。如果你的 CDN 支持,更好的做法是:

  • 签名 Cookie(如 CloudFront Signed Cookies):鉴权信息放 Cookie,浏览器自动带上, 刷新 Cookie 不需要动播放器
  • 路径级签名:签的是 /live/room-123/* 整个路径而非单个文件,分片天然覆盖

这两种方案的共同好处:续期不需要重新 load,用户完全无感

3. 分片和播放列表要用同一套签名策略

常见错误:只给 .m3u8 签了名,.ts 分片走另一条不鉴权的路径—— 那你的鉴权形同虚设,别人抓到分片 URL 就能直接下载。

反过来,如果 .m3u8 签名有效期长而 .ts 短,就会出现"能打开但播不动"。

4. 显式传 type

签名 URL 常常带一堆 query 参数,甚至路径里没有 .m3u8 后缀。 SDK 从后缀猜格式会猜错,建议一律显式指定:

js
{ url: signedUrl, type: 'hls' }   // 别让它猜

5. TTL 别设太长到失去意义

上面一直在说"设长点",但也别设成 30 天——那和不鉴权区别不大, URL 一旦泄露就是长期敞开。

推荐区间:够覆盖一次完整观看 + 合理余量,通常 6~24 小时是个平衡点。 如果你需要更严格的控制,走方案 2 的 Cookie 鉴权,而不是缩短 URL 签名 TTL。


六、其他要知道的

  • URL 在所有接入方式下都是明文可见的。用户 F12 / 抓包就能拿到带 token 的完整 URL。 签名 URL 防的是"未授权用户直接访问",防不了"已授权用户把链接发给别人"。 真要防转发,得靠短 TTL + IP 绑定 + 单点播控,这些都在 CDN 侧做。
  • 跨域要配 CORS。hls.js / flv.js 通过 JS 拉流,受同源策略约束, CDN 必须返回正确的 Access-Control-Allow-Origin。 ⚠️ 配错时拿不到专门的 CORS 错误码 —— xgplayer 把它报成普通网络错误, 你会收到 E_NETWORK。真正确认要看 DevTools Console 里浏览器自己的 CORS 报错。
  • ⚠️ 「iPhone 能播、Chrome 报 CORS」这条经验在 ADR-056 之后不再成立。ADR-056 之后 iOS ≥ 17.1 也走 hls.js(经 ManagedMediaSource), 分片同样由 JS 拉取、同样受同源策略约束 —— CDN 少配 CORS 的话 iPhone 会跟着一起报错。 只有 iOS < 17.1 回落原生 HLS 那一档才不走 CORS。 换句话说:CORS 现在是全平台必配项,别再拿 iPhone 能播当"配好了"的证据。

相关