PHP项目视频播放地址如何加密临时生成

wen PHP项目 22

本文目录导读:

PHP项目视频播放地址如何加密临时生成

  1. 基于Token的临时URL(推荐)
  2. Nginx + 鉴权模块方案
  3. 使用CDN Token鉴权
  4. 综合安全建议
  5. 完整的前端集成

在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();
    }
}
  1. Token过期时间:建议15-30分钟,根据视频长度调整
  2. 绑定客户端信息:IP、User-Agent增加安全性
  3. 频率限制:防止暴力破解
  4. 日志记录:记录所有访问请求
  5. HTTPS:强制使用加密传输
  6. CDN支持:利用CDN的Token鉴权功能
  7. 数据库存储:临时Token存储在Redis等高性能缓存中
  8. 断点续传支持:提供更好的用户体验

选择哪种方案取决于你的具体需求、服务器环境和安全等级要求,对于大多数应用场景,基于Token的临时URL + Nginx鉴权是性价比较高的选择。

抱歉,评论功能暂时关闭!