错误:使用Laravel权限急切加载角色和用户时

huangapple go评论65阅读模式
英文:

Error eager loading roles with users laravel permissions

问题

When attempting to eager load roles with their assigned users from Spatie's laravel-permissions library like this:

use Spatie\Permission\Models\Role;

Role::with('users')->get();

This error occurs:

Error: Class name must be a valid object or a string in file vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php on line 791

The code above works just fine in Laravel's PsySH powered Repl Tinker, as mentioned in this StackOverflow post: StackOverflow post.

So I thought if this only happens in HTTP requests, it must be due to a middleware issue.

I am using Laravel Sanctum for API authentication, and thus the route is under the middleware auth:sanctum in routes/api.php:

Route::middleware('auth:sanctum')->group(function () {
  Route::get('/roles', [RolesController::class, 'index']);
}

It also works if I move the route out of the middleware, but I shouldn't, since only authenticated users should be able to access that endpoint.

I'm guessing this has something to do with the fact that roles get created with the guard_name as web in the database by default, but I'm not sure how to fix it.

英文:

When attempting to eager load roles with their assigned users from Spatie's laravel-permissions library like this

use Spatie\Permission\Models\Role;

Role::with('users')->get();

This error occurs
> Error: Class name must be a valid object or a string in file vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php on line 791

The code above works just fine in the Laravel's PsySH powered Repl Tinker, see this StackOverflow post

So I thought if this only happens in HTTP requests, it must be due to a middleware issue

Am using Laravel Sanctum for API authentication and thus the route is under the middleware auth:sanctum

in routes/api.php

Route::middleware('auth:sanctum')->group(function () {
  Route::get('/roles', [RolesController::class, 'index']);
});

It also works if I move the route out of the middleware but I shouldn't, since only authenticated users should be able to access that endpoint

Am guessing this has something to do with the fact that roles get created with the guard_name as web in the database by default, but am not sure how to fix it

答案1

得分: 0

简要概述

config/auth.php 文件中,将以下内容添加到 guards 数组中:

'sanctum' => [
  'driver' => 'sanctum',
  'provider' => 'users'
],

解释

问题在于 Sanctum 的服务提供程序为提供者设置了一个值为 null 的身份验证守卫,请参阅源代码

class SanctumServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        config([
            'auth.guards.sanctum' => array_merge([
                'driver' => 'sanctum',
                'provider' => null, // <=== 这是问题所在
            ], config('auth.guards.sanctum', [])),
        ]);

        if (! app()->configurationIsCached()) {
            $this->mergeConfigFrom(__DIR__.'/../config/sanctum.php', 'sanctum');
        }
    }

这导致 Laravel-Permission 的 getModelForGuard() 辅助函数 在这里

function getModelForGuard(string $guard)
{
    return collect(config('auth.guards'))
        ->map(fn ($guard) => isset($guard['provider']) ? config("auth.providers.{$guard['provider']}.model") : null)
        ->get($guard);
}

在需要对 Sanctum 守卫进行身份验证的中间件时,返回空值,因此预加载失败 在这里

/**
 * A role belongs to some users of the model associated with its guard.
 */
public function users(): BelongsToMany
{
    return $this->morphedByMany(
        // Error occurs here
        getModelForGuard($this->attributes['guard_name'] ?? config('auth.defaults.guard')),
        'model',
        config('permission.table_names.model_has_roles'),
        app(PermissionRegistrar::class)->pivotRole,
        config('permission.column_names.model_morph_key')
    );
}

auth/config.php 中手动插入具有适当提供者的守卫后,预加载将再次正常工作

'guards' => [
    ...

    'sanctum' => [
        'driver' => 'sanctum',
        'provider' => 'users'
    ]
],
英文:

TL;DR?

in config/auth.php add this to the guards array

&#39;sanctum&#39; =&gt; [
  &#39;driver&#39; =&gt; &#39;sanctum&#39;,
  &#39;provider&#39; =&gt; &#39;users&#39;
],

Explanation

The problem is that Sanctum's service provider sets an authentication guard with a null value for the provider, see source code here

class SanctumServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        config([
            &#39;auth.guards.sanctum&#39; =&gt; array_merge([
                &#39;driver&#39; =&gt; &#39;sanctum&#39;,
                &#39;provider&#39; =&gt; null, // &lt;=== This is the issue
            ], config(&#39;auth.guards.sanctum&#39;, [])),
        ]);

        if (! app()-&gt;configurationIsCached()) {
            $this-&gt;mergeConfigFrom(__DIR__.&#39;/../config/sanctum.php&#39;, &#39;sanctum&#39;);
        }
    }

This causes Laravel-Permission's getModelForGuard() helper function here

function getModelForGuard(string $guard)
{
    return collect(config(&#39;auth.guards&#39;))
        -&gt;map(fn ($guard) =&gt; isset($guard[&#39;provider&#39;]) ? config(&quot;auth.providers.{$guard[&#39;provider&#39;]}.model&quot;) : null)
        -&gt;get($guard);
}

to return nothing when middleware requires the Sanctum guard to be authenticated, hence the eager load fails here

/**
 * A role belongs to some users of the model associated with its guard.
 */
public function users(): BelongsToMany
{
    return $this-&gt;morphedByMany(
        // Error occurs here
        getModelForGuard($this-&gt;attributes[&#39;guard_name&#39;] ?? config(&#39;auth.defaults.guard&#39;)),
        &#39;model&#39;,
        config(&#39;permission.table_names.model_has_roles&#39;),
        app(PermissionRegistrar::class)-&gt;pivotRole,
        config(&#39;permission.column_names.model_morph_key&#39;)
    );
}

After manually inserting the guard in auth/config.php with a proper provider, eager loading works again

&#39;guards&#39; =&gt; [
    ...

    &#39;sanctum&#39; =&gt; [
        &#39;driver&#39; =&gt; &#39;sanctum&#39;,
        &#39;provider&#39; =&gt; &#39;users&#39;
    ]
],

huangapple
  • 本文由 发表于 2023年5月14日 01:05:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/76243976.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定