You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Bunny.net CDN令牌认证对.m3u8文件失效问题求助

问题:HLS播放列表URL签名后返回403错误,但缩略图URL正常

我正在实现基于HTTP Live Streaming (HLS)的视频流方案,用Node.js给HLS播放列表URL签名时,使用的是CDN管理界面「Stream > Security > Token Authentication Key」区域获取的密钥。

但按照指定流程完成签名后,访问.m3u8播放列表URL时始终返回HTTP 403禁止错误。奇怪的是,将相同的签名流程应用到视频缩略图(如thumbnail.jpg)的URL上时,却能正常访问文件——这说明令牌生成和认证流程本身是有效的。

已尝试的解决措施:

  • 改用「CDN > Security > Token Authentication Key」区域的Token Authentication Key
  • 切换到CDN令牌认证(基础版),但仍只有缩略图可正常访问,.m3u8文件依旧返回403

以下是对thumbnail.jpg有效但对playlist.m3u8无效的两段代码:

var crypto = require('crypto'),
  securityKey = 'key_from_stream_security',
  path = '/a1640499-52ef-4721-9ba9-4e659cada6f5/playlist.m3u8';

// 设置过期时间为4天后
var expires = 1688885053;

var hashableBase = securityKey + path + expires;

// 生成并编码令牌
var md5String = crypto.createHash('md5').update(hashableBase).digest('binary');
var token = new Buffer(md5String, 'binary').toString('base64');
token = token.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, '');

// 生成最终URL
var url =
  'https://vz-9ac10aa0-92d.b-cdn.net' +
  path +
  '?token=' +
  token +
  '&expires=' +
  expires;

console.log(url);
var queryString = require('querystring');
var crypto = require('crypto');
var URL = require('url').URL; // 引入URL构造器

function addCountries(url, a, b) {
  var tempUrl = url;
  if (a != null) {
    var tempUrlOne = new URL(tempUrl);
    tempUrl += (tempUrlOne.search == '' ? '?' : '&') + 'token_countries=' + a;
  }
  if (b != null) {
    var tempUrlTwo = new URL(tempUrl);
    tempUrl +=
      (tempUrlTwo.search == '' ? '?' : '&') + 'token_countries_blocked=' + b;
  }
  return tempUrl;
}

function signUrl(
  url,
  securityKey,
  expirationTime = 3600,
  userIp,
  isDirectory = false,
  pathAllowed,
  countriesAllowed,
  countriesBlocked,
) {
  /*
    url: 不带末尾斜杠的CDN URL - 示例: http://test.b-cdn.net/file.png
    securityKey: 拉取区域中找到的安全令牌
    expirationTime: 认证有效期(默认: 86400秒/24小时)
    userIp: 若启用用户IP功能则为可选参数
    isDirectory: 可选参数 - 设为"true"时返回用斜杠分隔的URL(示例: (domain)/bcdn_token=...)
    pathAllowed: 要认证的目录(示例: /path/to/images)
    countriesAllowed: 允许访问的国家列表(示例: CA, US, TH)
    countriesBlocked: 禁止访问的国家列表(示例: CA, US, TH)
  */
  var parameterData = '',
    parameterDataUrl = '',
    signaturePath = '',
    hashableBase = '',
    token = '';
  var expires = expirationTime;
  var url = addCountries(url, countriesAllowed, countriesBlocked);
  var parsedUrl = new URL(url);
  var parameters = new URL(url).searchParams;
  if (pathAllowed != '') {
    signaturePath = pathAllowed;
    parameters.set('token_path', signaturePath);
  } else {
    signaturePath = decodeURIComponent(parsedUrl.pathname);
  }

  console.log(parsedUrl.pathname);
  parameters.sort();
  if (Array.from(parameters).length > 0) {
    parameters.forEach(function (value, key) {
      if (value == '') {
        return;
      }
      if (parameterData.length > 0) {
        parameterData += '&';
      }
      parameterData += key + '=' + value;
      parameterDataUrl += '&' + key + '=' + queryString.escape(value);
    });
  }
  hashableBase =
    securityKey +
    signaturePath +
    expires +
    (userIp != null ? userIp : '') +
    parameterData;
  token = Buffer.from(
    crypto.createHash('sha256').update(hashableBase).digest(),
  ).toString('base64');
  token = token
    .replace(/\n/g, '')
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=/g, '');
  if (isDirectory) {
    return (
      parsedUrl.protocol +
      '//' +
      parsedUrl.host +
      '/bcdn_token=' +
      token +
      parameterDataUrl +
      '&expires=' +
      expires +
      parsedUrl.pathname
    );
  } else {
    return (
      parsedUrl.protocol +
      '//' +
      parsedUrl.host +
      parsedUrl.pathname +
      '?token=' +
      token +
      parameterDataUrl +
      '&expires=' +
      expires
    );
  }
}

console.log(
  signUrl(
    'https://vz-9ac10aa0-92d.b-cdn.net/548789ea-d0d7-4dac-b656-cc2acedf0f5c/playlist.m3u8',
    'key_from_stream_security',
    1688885053,
    '',
    false,
    '/548789ea-d0d7-4dac-b656-cc2acedf0f5c/playlist.m3u8',
  ),
);

内容的提问来源于stack exchange,提问作者Ghayoor ul Haq

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 04:40:36