Laravel 5.4:public目录用户视频未登录可访问的权限控制问题咨询
这个问题的核心原因很明确:public目录是Web服务器(Nginx/Apache)直接对外开放的,所有请求都会被服务器直接处理,完全绕过了Laravel的权限中间件。所以要解决这个问题,我们需要把用户的视频文件移出public目录,通过Laravel来接管文件的访问请求,从而实现权限校验。
下面是具体的实现步骤:
1. 迁移用户视频文件夹到非公开目录
首先,把原来放在public/{userId}的用户视频文件夹,移动到storage/app/users_videos/{userId}目录下。这个目录默认是Web服务器无法直接访问的,完美避开了无权限访问的问题。
如果管理员是通过FTP上传文件,你需要调整FTP的根目录指向storage/app/users_videos,或者让管理员上传后手动/自动将文件移动到这个目录。
2. 创建受权限保护的路由
在routes/web.php中添加一个路由,用auth中间件保护,确保只有登录用户才能访问:
Route::get('/user-videos/{userId}/{filename}', 'UserVideoController@show') ->middleware('auth') ->name('user.videos.show');
3. 编写控制器处理文件访问与权限校验
创建UserVideoController,在里面实现权限判断和文件返回逻辑:
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\Storage; use Illuminate\Http\Response; class UserVideoController extends Controller { public function show($userId, $filename) { // 权限校验逻辑:两种情况允许访问 // 1. 当前登录用户是管理员(假设你的User模型有isAdmin()方法) // 2. 当前登录用户就是文件所属的用户 $currentUser = auth()->user(); if (!$currentUser->isAdmin() && $currentUser->id != $userId) { abort(403, '你没有权限访问该文件'); } // 拼接文件路径 $filePath = "users_videos/{$userId}/{$filename}"; // 检查文件是否存在 if (!Storage::exists($filePath)) { abort(404, '文件不存在'); } // 获取文件的绝对路径 $absolutePath = storage_path("app/{$filePath}"); // 返回文件,Laravel会自动识别MIME类型并设置响应头 return response()->file($absolutePath); // 如果需要让浏览器下载而不是在线播放,可以用download方法: // return Storage::download($filePath, $filename); } }
注意:如果你的User模型没有
isAdmin()方法,需要根据你的权限体系调整校验逻辑,比如检查用户的role字段是否为管理员。
4. 调整前端/管理员的访问链接
原来直接访问public/123/video.mp4的链接,现在要改成路由对应的地址,比如/user-videos/123/video.mp4。这样所有的视频请求都会经过Laravel的权限校验。
可选:优化Web服务器配置(如果不想移动文件)
如果因为某些原因无法移动文件到storage目录,你可以通过Web服务器配置,把用户视频的请求转发到Laravel处理。以Nginx为例,在站点配置中添加:
location ~ ^/[0-9]+/.*\.(mp4|avi|mov)$ { rewrite ^/(.*)$ /user-videos/$1 last; }
这个规则会把所有类似/123/video.mp4的请求转发到/user-videos/123/video.mp4,然后由Laravel的路由处理权限校验。不过这种方法不如移动文件到storage目录安全,因为如果服务器配置出错,可能会导致文件直接暴露。
额外注意事项
- 确保
storage/app/users_videos目录有正确的读写权限,Laravel需要能读取这个目录下的文件。 - 如果使用云存储(比如AWS S3),可以直接用Laravel的Storage门面处理,权限逻辑完全一致,只需要调整文件路径的前缀。
- 可以添加缓存头优化视频加载速度,比如在返回文件时添加
Cache-Control响应头:return response()->file($absolutePath)->header('Cache-Control', 'public, max-age=31536000');
这样处理后,只有登录且有权限的用户才能访问对应的视频文件,完美解决了原问题。
内容的提问来源于stack exchange,提问作者hatemjapo

