核心在于“怎么锁”:需用自定义Auth中间件实现资源级权限校验,提取ID查订单状态,静态资源须经签名token验证后流式输出,所有接口必须独立三重校验。

ThinkPHP 实现知识付费系统,核心不在“怎么卖”,而在于“怎么锁”——即内容是否真正隔离、权限是否不可绕过、解锁逻辑是否能抗调试和重放。硬编码判断、前端控制、简单 session 标记,全都不够用。
为什么用 Auth 中间件做内容解锁会失效
很多人直接在控制器里写 if (!$this->auth->check()) { $this->error('请先购买'); },这看似合理,但实际漏洞明显:
— 权限检查只发生在控制器入口,中间件未覆盖路由绑定、模型查询、视图渲染等环节
— 若用户手动构造 URL 访问未授权资源(如 /course/123/video/456),且该接口未显式调用 check(),就直接返回了
— 更严重的是,$this->auth 依赖 session 或 token,而 session 可被复用、token 可被截获重放,缺乏业务粒度校验
think\middleware\Auth 中间件必须配合资源级策略注册
真正的解锁逻辑不能靠“有没有登录”,而要落到“当前用户对这个课程/章节/文档是否有有效购买记录”。
需在中间件中完成三件事:
— 提取请求路径中的资源 ID(如 $courseId = $request->param('id'))
— 查询数据库确认该用户对该资源的订单状态是否为 paid 且未过期
— 若不满足,直接 return json(['code'=>403, 'msg'=>'内容未解锁']) 并终止后续流程
注意:think\middleware\Auth 默认只做身份认证,不处理业务权限;必须继承它并重写 handle(),或新建中间件注册到对应路由组
静态资源(PDF/MP4)如何防止直链盗用
知识付费最常被绕过的点就是静态文件:用户拿到 /uploads/course/123/lesson.pdf 后,分享链接即可无限传播。
解决方式不是藏路径,而是断开“请求 URL ↔ 文件物理路径”的映射:
— 所有资源访问必须走统一入口,例如 /resource/download?token=xxx&rid=456
— token 由服务端签发,含用户 ID、资源 ID、过期时间、签名(如 hash_hmac('sha256', "$uid|$rid|$exp", $secret))
— 控制器验证 token 有效性后,用 readfile() 输出文件流,并设置 Content-Disposition: attachment,禁止浏览器缓存
— 绝对不要用 header('Location: /real/path.pdf') 跳转,那等于把真实路径暴露给客户端
前端展示的“已解锁”状态不能信,后端必须重复校验
前端常通过 AJAX 获取用户已购列表,再用 JS 控制按钮显隐或跳转逻辑。这完全不可靠:
— 用户可禁用 JS、篡改 localStorage、伪造响应数据
— 即使加了 loading 和防重复点击,只要后端接口没做二次鉴权,就等于裸奔
— 某些接口(如获取视频播放地址)看似只是“返回一个 URL”,但其实那个 URL 本身应是临时有效的一次性签名地址(类似 AWS S3 presigned URL),而非固定路径
关键点:每个涉及付费内容的操作接口,都必须独立完成「用户身份 + 资源归属 + 支付状态」三重校验,不能复用上一个接口的结果或前端传来的 flag
立即学习“PHP免费学习笔记(深入)”;



















