You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Laravel中基于用户权限过滤注入InertiaJS的Ziggy路由?

问题描述

我正在开发一个基于Laravel、InertiaJS和ReactJS的项目,通过Ziggy Routes向前端注入路由,同时使用了Spatie/Laravel-Permissions权限包。目前的问题是Ziggy会把所有路由都注入到前端,存在潜在安全风险。我需要过滤注入的路由,只保留用户有权访问的部分:

  • 所有client.*前缀的路由都允许访问
  • admin.*前缀的路由需要根据用户权限筛选
  • 同时项目中还有fortify、horizon及debugbar相关路由需要处理

当前HandleInertiaRequests.php中间件的代码如下:

public function share(Request $request): array
{
    return [
        ...parent::share($request),
        'auth' => [
            'user' => $request->user(),
            'roles' => Auth::check() ? Auth::user()->getRoleNames() : [],
            'permissions' => Auth::check() ? Auth::user()->getAllPermissions() : [],
        ],
        'flash' => function () use ($request) {
            return [
                'success' => $request->session()->get('success'),
                'error' => $request->session()->get('error'),
            ];
        },
        'ziggy' => fn () => [
            ...(new Ziggy)->toArray(),
            'location' => $request->url(),
        ],
        'csrf_token' => csrf_token(),
    ];
}

最佳实现方案

核心思路是在Ziggy生成路由数组后,根据用户权限和路由命名规则对路由进行过滤,只保留符合条件的路由注入到前端。

步骤1:编写路由过滤逻辑

在HandleInertiaRequests类中新增路由过滤方法,针对不同前缀的路由设置差异化规则:

  1. client.*:直接保留所有该前缀路由
  2. admin.*:匹配用户权限(可根据自身权限命名规则调整匹配逻辑)
  3. fortify路由:保留认证相关的公开路由,登录用户可访问全部fortify路由
  4. horizon/debugbar路由:仅在本地开发环境保留,生产环境直接过滤
  5. 未登录用户:仅保留公开路由(如fortify登录、注册)和client.*中的公开路由(按需调整)

步骤2:修改HandleInertiaRequests代码

修改后的完整代码示例:

use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use Ziggy\Ziggy;

public function share(Request $request): array
{
    return [
        ...parent::share($request),
        'auth' => [
            'user' => $request->user(),
            'roles' => Auth::check() ? Auth::user()->getRoleNames() : [],
            'permissions' => Auth::check() ? Auth::user()->getAllPermissions()->pluck('name')->toArray() : [],
        ],
        'flash' => function () use ($request) {
            return [
                'success' => $request->session()->get('success'),
                'error' => $request->session()->get('error'),
            ];
        },
        'ziggy' => fn () => [
            ...$this->filterZiggyRoutes(),
            'location' => $request->url(),
        ],
        'csrf_token' => csrf_token(),
    ];
}

/**
 * 过滤Ziggy路由,仅保留用户有权访问的路由
 */
private function filterZiggyRoutes(): array
{
    $ziggy = new Ziggy;
    $allRoutes = $ziggy->toArray()['routes'];
    $filteredRoutes = [];
    $isLoggedIn = Auth::check();
    $permissions = $isLoggedIn ? Auth::user()->getAllPermissions()->pluck('name')->toArray() : [];

    foreach ($allRoutes as $name => $route) {
        // 保留所有client.*前缀路由
        if (Str::startsWith($name, 'client.')) {
            $filteredRoutes[$name] = $route;
            continue;
        }

        // 过滤admin.*路由:检查用户是否拥有对应权限
        if (Str::startsWith($name, 'admin.')) {
            // 示例:权限命名规则为"access_"+路由名称(替换点为下划线),可按需调整
            $requiredPermission = 'access_' . str_replace('.', '_', $name);
            if ($isLoggedIn && in_array($requiredPermission, $permissions)) {
                $filteredRoutes[$name] = $route;
            }
            continue;
        }

        // 处理fortify路由:保留公开认证路由,登录用户可访问全部
        if (Str::startsWith($name, 'fortify.')) {
            $publicFortifyRoutes = ['fortify.login', 'fortify.register', 'fortify.password.request', 'fortify.password.email'];
            if (in_array($name, $publicFortifyRoutes) || $isLoggedIn) {
                $filteredRoutes[$name] = $route;
            }
            continue;
        }

        // 仅本地环境保留horizon和debugbar路由
        if (Str::startsWith($name, 'horizon.') || Str::startsWith($name, 'debugbar.')) {
            if (app()->environment('local')) {
                $filteredRoutes[$name] = $route;
            }
            continue;
        }

        // 其他路由:可添加自定义规则,比如保留所有GET方法的公开路由
        // if (in_array('GET', $route['methods']) && !Str::startsWith($name, 'admin.')) {
        //     $filteredRoutes[$name] = $route;
        // }
    }

    return [
        'routes' => $filteredRoutes,
        'url' => $ziggy->url(),
        'port' => $ziggy->port(),
        'domain' => $ziggy->domain(),
        'prefix' => $ziggy->prefix(),
        'defaults' => $ziggy->defaults(),
    ];
}

关键说明

  • 权限匹配逻辑:示例中假设权限命名为access_admin_dashboard对应路由admin.dashboard,你可以根据项目实际权限规则调整$requiredPermission的生成方式,比如直接用路由名称作为权限名,或者维护路由-权限映射表。
  • 环境控制:horizon和debugbar路由仅在本地环境暴露,避免生产环境泄露敏感运维路由。
  • 性能优化:如果项目路由数量庞大,可以考虑缓存用户有权访问的路由列表,减少每次请求的过滤计算开销。

内容的提问来源于stack exchange,提问作者Emre Can Çakıroğlu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 13:00:54