Laravel排序字段白名单吗

wen PHP项目 5

本文目录导读:

Laravel排序字段白名单吗

  1. 方法一:手动白名单数组(推荐)
  2. 方法二:使用 Laravel 查询作用域(封装)
  3. 方法三:使用第三方扩展包 spatie/laravel-query-builder
  4. 方法四:使用 Laravel 8+ 的模型 sortable Trait(简洁版)
  5. ⚠️ 需要特别防御的情况
  6. 总结建议

在 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 的参数,除非你先对字段名做了白名单校验。

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