本文目录导读:

- 核心权限模型设计
- 基础权限检查中间件
- 播放链接保护(核心安全措施)
- 视频防盗链(Referer / User-Agent 验证)
- 流媒体服务器级别的保护(生产环境推荐)
- 完整的前后端交互示例
- 安全最佳实践
- 性能优化建议
在PHP项目中实现点播视频的权限管控播放,通常需要结合用户认证、资源授权和播放链接保护三个层面,以下是几种常见的实现方案,从简单到复杂逐步说明:
核心权限模型设计
首先建立基本的用户-角色-资源模型:
-- 用户表
CREATE TABLE users (
id INT PRIMARY KEY,
username VARCHAR(50),
role ENUM('admin', 'vip', 'normal', 'free'),
expire_time DATETIME -- VIP 到期时间
);
-- 视频资源表
CREATE TABLE videos (
id INT PRIMARY KEY,VARCHAR(200),
file_path VARCHAR(500),
category VARCHAR(50),
price DECIMAL(10,2), -- 单点价格
is_free BOOLEAN DEFAULT false
);
-- 用户购买记录表
CREATE TABLE user_video_access (
id INT PRIMARY KEY,
user_id INT,
video_id INT,
purchase_time DATETIME,
expire_time DATETIME,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (video_id) REFERENCES videos(id)
);
基础权限检查中间件
// video_permission.php
function checkVideoPermission($userId, $videoId) {
// 1. 获取视频信息
$video = getVideoById($videoId);
// 2. 如果是免费视频,直接放行
if ($video['is_free']) {
return true;
}
// 3. 获取用户信息
$user = getUserById($userId);
// 4. 检查 VIP 权限
if ($user['role'] == 'vip' || $user['role'] == 'admin') {
// 检查 VIP 是否过期
if ($user['expire_time'] > date('Y-m-d H:i:s')) {
return true;
}
}
// 5. 检查单次购买权限
$access = getUserVideoAccess($userId, $videoId);
if ($access && $access['expire_time'] > date('Y-m-d H:i:s')) {
return true;
}
// 6. 检查用户等级是否满足视频等级要求
if (checkUserLevel($user['role'], $video['required_level'])) {
return true;
}
return false;
}
播放链接保护(核心安全措施)
直接暴露视频路径非常危险,需要使用动态签名URL:
方案A:简单 Token 验证
// 生成播放链接
function generateVideoUrl($videoId, $userId, $expireTime = 3600) {
$secretKey = 'your-secret-key-123'; // 服务端密钥
$timestamp = time() + $expireTime;
// 生成签名
$sign = md5($videoId . $userId . $timestamp . $secretKey);
// 构建播放地址
return "/play.php?v=" . $videoId .
"&t=" . $timestamp .
"&s=" . $sign .
"&u=" . $userId;
}
// 播放页面验证
// play.php
if (!validateVideoToken($_GET)) {
die('Access Denied');
}
function validateVideoToken($params) {
$secretKey = 'your-secret-key-123';
$videoId = $params['v'];
$userId = $params['u'];
$timestamp = $params['t'];
$sign = $params['s'];
// 验证时间是否过期
if ($timestamp < time()) {
return false;
}
// 验证签名是否匹配
$expectedSign = md5($videoId . $userId . $timestamp . $secretKey);
return $sign === $expectedSign;
}
方案B:使用 Nginx Secure Link(推荐)
在 Nginx 配置中启用 secure_link:
location /videos/ {
# 开启 secure_link
secure_link $arg_md5,$arg_expires;
secure_link_md5 "$secure_link_expires$uri$remote_addr secret_key";
if ($secure_link = "") {
return 403;
}
if ($secure_link = "0") {
return 410;
}
# 视频文件位置
alias /data/videos/;
mp4;
mp4_buffer_size 4M;
mp4_max_buffer_size 10M;
}
PHP 生成安全链接:
function generateSecureLink($videoPath) {
$secretKey = 'your-nginx-secret';
$expires = time() + 3600; // 1小时有效
// 生成 MD5 值
$md5 = base64_encode(md5($expires . $videoPath . $_SERVER['REMOTE_ADDR'] . $secretKey, true));
$md5 = strtr($md5, '+/', '-_');
$md5 = str_replace('=', '', $md5);
return "/videos" . $videoPath . "?md5=" . $md5 . "&expires=" . $expires;
}
视频防盗链(Referer / User-Agent 验证)
// 检查来源
function checkReferer() {
$allowedDomains = ['yourdomain.com', '*.yourdomain.com'];
$referer = $_SERVER['HTTP_REFERER'] ?? '';
foreach ($allowedDomains as $domain) {
if (strpos($referer, $domain) !== false) {
return true;
}
}
return false;
}
流媒体服务器级别的保护(生产环境推荐)
对于大规模点播,建议使用专业的流媒体服务器:
FFmpeg + Nginx RTMP 模块
application vod {
# 使用 HLS 协议
hls on;
hls_path /tmp/hls;
# 播放权限验证
on_play http://your-server.com/auth.php;
on_publish http://your-server.com/auth.php;
}
auth.php 验证脚本:
<?php
// 从请求中获取用户和视频信息
$userId = $_GET['user_id'] ?? '';
$videoId = $_GET['video_id'] ?? '';
$token = $_GET['token'] ?? '';
// 验证权限
if (checkVideoAccess($userId, $videoId, $token)) {
header('HTTP/1.0 200 OK');
echo 'Authorized';
} else {
header('HTTP/1.0 403 Forbidden');
echo 'Forbidden';
}
完整的前后端交互示例
// 前端请求播放
// JavaScript
function requestPlayVideo(videoId) {
fetch('/api/get-video-url', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
video_id: videoId,
user_token: localStorage.getItem('token')
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
// 使用 player.js 播放
const player = new Player({
url: data.play_url, // 带签名的临时链接
token: data.play_token
});
player.play();
} else {
alert('没有播放权限');
}
});
}
// 后端生成播放URL
// /api/get-video-url.php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$userId = validateUserToken($_POST['user_token']);
$videoId = $_POST['video_id'];
if (checkVideoPermission($userId, $videoId)) {
// 生成1小时有效的临时链接
$playUrl = generateTemporaryUrl($videoId, $userId, 3600);
echo json_encode([
'success' => true,
'play_url' => $playUrl,
'expires_in' => 3600
]);
} else {
http_response_code(403);
echo json_encode(['success' => false, 'message' => '无权限']);
}
}
安全最佳实践
- 使用临时链接:播放链接设置有效期(建议 1-4 小时)
- 单次验证:同一个链接不能多次使用
- 限制 IP:签名中包含用户 IP,防止链接盗用
- 水印技术:在视频中动态添加用户水印
- 加密存储:视频文件存储时加密,播放时解密
- CDN 防护:使用 CDN 的 Referer 防盗链和 IP 黑白名单
- 日志审计:记录所有播放请求,便于追溯
性能优化建议
- 缓存权限:使用 Redis 缓存权限检查结果
- 异步验证:大规模场景使用消息队列异步处理权限验证
- CDN 边缘计算:在 CDN 边缘节点进行权限验证
推荐的生产环境方案组合:
- 用户认证:JWT Token
- 权限检查:RBAC + 购买记录
- 播放保护:Nginx Secure Link + 时间戳签名
- 防盗链:Referer + User-Agent 检查
- 流媒体:HLS/MPEG-DASH + DRM(商业内容)
根据你的项目规模和安全要求,可以选择合适的方案组合,对于商业级内容,建议考虑专业的 DRM 解决方案(如 Widevine、PlayReady)和 CDN 防盗链服务。