本文目录导读:

在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鉴权是性价比较高的选择。