本文目录导读:

在PHP项目中为视频播放地址加密并实现临时生成,主要有以下几种常用方案,按安全级别从低到高排列:
基于Token的临时URL(推荐)
核心原理
- 生成带过期时间和签名的URL
- 服务端验证签名和有效期
- 过期即失效
实现代码
<?php
class VideoToken {
private $secretKey = 'your-secret-key-here';
/**
* 生成临时视频播放地址
* @param string $videoPath 视频文件路径
* @param int $expiryMinutes 过期时间(分钟)
* @return string 加密后的播放地址
*/
public function generateTempUrl($videoPath, $expiryMinutes = 30) {
// 生成唯一标识
$token = bin2hex(random_bytes(16));
$expireTime = time() + ($expiryMinutes * 60);
// 构建签名数据
$signData = [
'token' => $token,
'path' => $videoPath,
'expire' => $expireTime,
'ip' => $_SERVER['REMOTE_ADDR'] // 可选:绑定客户端IP
];
// 生成签名
$sign = $this->generateSign($signData);
// 构建加密URL
$params = http_build_query([
'token' => $token,
'expire' => $expireTime,
'sign' => $sign
]);
return "/play.php?{$params}";
}
/**
* 验证播放请求
* @return bool|string 失败返回false,成功返回视频路径
*/
public function validatePlayRequest() {
$token = $_GET['token'] ?? '';
$expire = $_GET['expire'] ?? 0;
$sign = $_GET['sign'] ?? '';
// 检查是否过期
if (time() > $expire) {
return false;
}
// 从数据库或缓存获取视频路径(根据token)
$videoPath = $this->getVideoPathByToken($token);
if (!$videoPath) {
return false;
}
// 验证签名
$signData = [
'token' => $token,
'path' => $videoPath,
'expire' => $expire,
'ip' => $_SERVER['REMOTE_ADDR']
];
if ($this->generateSign($signData) !== $sign) {
return false;
}
return $videoPath;
}
private function generateSign($data) {
ksort($data);
$signStr = http_build_query($data);
return hash_hmac('sha256', $signStr, $this->secretKey);
}
private function getVideoPathByToken($token) {
// 实际项目中应从数据库查询
// 示例:使用Redis存储临时映射
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
return $redis->get("video_token:{$token}");
}
}
// 使用示例
$videoToken = new VideoToken();
$tempUrl = $videoToken->generateTempUrl('/videos/movie.mp4', 30);
echo "临时播放地址:{$tempUrl}";
?>
播放接口实现 (play.php)
<?php
require_once 'VideoToken.php';
$videoToken = new VideoToken();
$videoPath = $videoToken->validatePlayRequest();
if ($videoPath === false) {
header('HTTP/1.0 403 Forbidden');
die('播放链接已过期');
}
// 设置视频流输出
$file = __DIR__ . $videoPath;
if (!file_exists($file)) {
header('HTTP/1.0 404 Not Found');
die('视频文件不存在');
}
// 支持断点续传
header('Content-Type: video/mp4');
header('Content-Length: ' . filesize($file));
header('Accept-Ranges: bytes');
header('Cache-Control: no-cache, must-revalidate');
// 输出文件
readfile($file);
?>
Nginx + 鉴权模块方案
Nginx配置
location /videos/ {
secure_link $arg_md5,$arg_expires;
secure_link_md5 "$secure_link_expires$uri$remote_addr secret";
if ($secure_link = "") {
return 403;
}
if ($secure_link = "0") {
return 410;
}
alias /data/videos/;
}
PHP生成地址
function generateNginxSecureUrl($videoPath, $expiryMinutes = 30) {
$secret = 'secret';
$expires = time() + ($expiryMinutes * 60);
$md5 = base64_encode(md5($expires . $videoPath . $_SERVER['REMOTE_ADDR'] . $secret, true));
$md5 = strtr($md5, '+/', '-_');
return "/videos/{$videoPath}?md5={$md5}&expires={$expires}";
}
使用CDN Token鉴权
阿里云CDN示例
function generateAliCDNUrl($videoUrl, $key, $expiryMinutes = 30) {
$expireTime = time() + ($expiryMinutes * 60);
$path = parse_url($videoUrl, PHP_URL_PATH);
// 生成鉴权串
$sign = md5($path . '-' . $expireTime . '-0-0-' . $key);
$authKey = "auth_key={$expireTime}-0-0-{$sign}";
return $videoUrl . (strpos($videoUrl, '?') ? '&' : '?') . $authKey;
}
综合安全建议
多层加密方案
class SecureVideoPlayer {
// 1. 双重验证
public function generateDoubleSecureUrl($videoPath) {
// 第一层:服务端token
$tempToken = $this->generateTempUrl($videoPath, 5); // 5分钟过期
// 第二层:客户端加密(前端配合)
$encryptedParam = $this->clientEncrypt($tempToken);
return "/play?param=" . urlencode($encryptedParam);
}
// 2. IP绑定 + 频率限制
private function addSecurityConstraints() {
// 记录每个IP的请求次数
$redis->incr("ip_count:{$ip}");
$redis->expire("ip_count:{$ip}", 60);
if ($redis->get("ip_count:{$ip}") > 10) {
// 每分钟超过10次请求,拒绝
return false;
}
}
// 3. User-Agent验证
private function validateUserAgent() {
$allowedUA = ['Mozilla', 'Chrome', 'Safari'];
$ua = $_SERVER['HTTP_USER_AGENT'];
$matched = false;
foreach ($allowedUA as $pattern) {
if (stripos($ua, $pattern) !== false) {
$matched = true;
break;
}
}
return $matched;
}
}
完整的前端集成
JavaScript客户端
class VideoPlayer {
constructor() {
this.tokenRefreshInterval = null;
}
async loadVideo() {
// 获取临时播放地址
const response = await fetch('/api/get-temp-url.php');
const data = await response.json();
// 初始化播放器
this.player = new Plyr('#video-player', {
controls: ['play', 'progress', 'current-time', 'volume']
});
// 设置视频源
this.player.source = {
type: 'video',
sources: [{
src: data.url,
type: 'video/mp4'
}]
};
// 定时刷新token(在过期前刷新)
this.startTokenRefresh(data.expires_in);
}
startTokenRefresh(expiresIn) {
// 在过期前1分钟刷新
const refreshTime = (expiresIn - 60) * 1000;
this.tokenRefreshInterval = setInterval(async () => {
const newUrl = await this.refreshToken();
this.updateVideoSource(newUrl);
}, refreshTime);
}
async refreshToken() {
const response = await fetch('/api/refresh-token.php', {
method: 'POST',
headers: {
'X-CSRF-Token': csrfToken
}
});
return response.json();
}
}
- Token过期时间:建议15-30分钟,根据视频长度调整
- 绑定客户端信息:IP、User-Agent增加安全性
- 频率限制:防止暴力破解
- 日志记录:记录所有访问请求
- HTTPS:强制使用加密传输
- CDN支持:利用CDN的Token鉴权功能
- 数据库存储:临时Token存储在Redis等高性能缓存中
- 断点续传支持:提供更好的用户体验
选择哪种方案取决于你的具体需求、服务器环境和安全等级要求,对于大多数应用场景,基于Token的临时URL + Nginx鉴权是性价比较高的选择。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播