本文目录导读:

- 方法一:手动白名单数组(推荐)
- 方法二:使用 Laravel 查询作用域(封装)
- 方法三:使用第三方扩展包
spatie/laravel-query-builder - 方法四:使用 Laravel 8+ 的模型
sortableTrait(简洁版) - ⚠️ 需要特别防御的情况
- 总结建议
在 Laravel 中,排序字段默认是没有白名单机制的,直接通过用户输入进行排序存在 SQL 注入风险(虽然 Laravel 的查询构建器有参数绑定,但表名、列名无法被绑定)。
如果你允许用户传递 sort 参数,必须手动实现白名单校验,以下是几种推荐的安全实现方式:
手动白名单数组(推荐)
这是最安全、最常用的方式,明确指定允许排序的字段列表。
use Illuminate\Http\Request;
public function index(Request $request)
{
// 定义白名单
$allowedSorts = ['id', 'name', 'created_at', 'price'];
// 获取用户输入,默认按 id 降序
$sortField = $request->input('sort', 'id');
$sortDirection = $request->input('direction', 'desc');
// 检查是否在白名单内
if (!in_array($sortField, $allowedSorts)) {
$sortField = 'id'; // 或返回错误
}
// 检查排序方向(只允许 asc 或 desc)
$sortDirection = in_array($sortDirection, ['asc', 'desc']) ? $sortDirection : 'desc';
$users = User::orderBy($sortField, $sortDirection)->get();
return response()->json($users);
}
使用 Laravel 查询作用域(封装)
将排序逻辑封装在模型中,便于复用。
// 在 User 模型中定义 scope
public function scopeSortByWhiteList($query, $sortField, $sortDirection = 'desc')
{
$allowedSorts = ['id', 'name', 'email', 'created_at'];
if (!in_array($sortField, $allowedSorts)) {
$sortField = 'id';
}
$sortDirection = in_array($sortDirection, ['asc', 'desc']) ? $sortDirection : 'desc';
return $query->orderBy($sortField, $sortDirection);
}
// 在控制器中使用
$users = User::sortByWhiteList($request->sort, $request->direction)->get();
使用第三方扩展包 spatie/laravel-query-builder
这是最专业、最完善的解决方案,自带白名单、多字段排序、过滤等功能。
安装:
composer require spatie/laravel-query-builder
使用:
use Spatie\QueryBuilder\QueryBuilder;
public function index()
{
$users = QueryBuilder::for(User::class)
->allowedSorts(['name', 'email', 'created_at']) // 白名单
->defaultSort('created_at') // 默认排序
->get();
return response()->json($users);
}
前端传参方式:
GET /users?sort=name // 正序
GET /users?sort=-name // 降序(带 -)
GET /users?sort=name,-email // 多字段排序
使用 Laravel 8+ 的模型 sortable Trait(简洁版)
如果项目简单,可以自己写一个 Trait:
trait HasSortableFields
{
public function scopeSortable($query, $field, $direction = 'desc')
{
$allowedSorts = property_exists($this, 'sortable') ? $this->sortable : ['id'];
if (!in_array($field, $allowedSorts)) {
return $query;
}
$direction = in_array($direction, ['asc', 'desc']) ? $direction : 'desc';
return $query->orderBy($field, $direction);
}
}
// 在模型中使用
class User extends Model
{
use HasSortableFields;
protected $sortable = ['id', 'name', 'email', 'created_at'];
}
// 控制器
$users = User::sortable($request->sort, $request->direction)->get();
⚠️ 需要特别防御的情况
不要对以下类型的参数使用字符串拼接或 orderByRaw:
// ❌ 危险:直接拼接用户输入 $query->orderBy($request->sort); // 即使 $request->sort 是字符串,也可能被注入别名 // ✅ 安全:使用白名单 $query->orderBy($allowedField, $direction);
总结建议
- 简单项目:手写白名单数组(方法一)
- 中等项目:使用 Trait(方法四)
- 复杂项目/API 接口:使用
spatie/laravel-query-builder(方法三) - 这是目前社区最推荐的做法。
核心原则:永远不要让用户直接控制字段名作为 orderBy 的参数,除非你先对字段名做了白名单校验。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播