Laravel 政策从来没有调用过总是 403

Laravel policy never called always 403

Laravel 被用作 API 端点,我试图控制用户根据用户类型查看网站某些部分的能力。使用 $this->authorizeResource()$this->authorize() 它总是 returns 403 而没有达到政策。注释掉应用程序允许访问的行。

AuthServiceProvider

<?php

namespace Project\Providers;

use Project\Entities\Plumber;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Auth;
use Project\Policies\PlumberPolicy;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        Plumber::class => PlumberPolicy::class
    ];

    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();
    }
}

PlumberPolicy,为了测试,一切都 returns 为真。

<?php

namespace Project\Policies;

use Project\Entities\User;
use Project\Entities\Plumber;
use Illuminate\Auth\Access\HandlesAuthorization;

class PlumberPolicy
{
    use HandlesAuthorization;

    /**
     * Determine whether the user can view the Plumber.
     *
     * @param  \Project\Entities\User  $user
     * @param  \Project\Entities\Plumber  $plumber
     * @return mixed
     */
    public function view(User $user, Plumber $plumber)
    {
        //
        return true;
    }

    /**
     * Determine whether the user can create Plumbers.
     *
     * @param  \Project\Entities\User  $user
     * @return mixed
     */
    public function create(User $user)
    {
        //
        return true;
    }

    /**
     * Determine whether the user can update the Plumber.
     *
     * @param  \Project\Entities\User  $user
     * @param  \Project\Entities\Plumber  $plumber
     * @return mixed
     */
    public function update(User $user, Plumber $plumber)
    {
        //
        return true;
    }

    /**
     * Determine whether the user can delete the Plumber.
     *
     * @param  \Project\Entities\User  $user
     * @param  \Project\Entities\Plumber  $plumber
     * @return mixed
     */
    public function delete(User $user, Plumber $plumber)
    {
        //
        return true;
    }
}

控制器设置。

namespace Project\Http\Controllers;
class Controller extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}

namespace Project\Http\Controllers;
class ApiController extends Controller
{

    public function index(Request $request) { .... }
    public function show(Request $request, $id) { .... }
    public function store(Request $request) { .... }
    public function create(Request $request) { .... }
    public function edit(Request $request) { .... }
    public function update(Request $request, $id) { .... }
    public function destroy(Request $request, $id) { .... }
}

namespace Project\Http\Controllers;
class PlumberController extends ApiController {
    public function __construct() {
        $this->authorizeResource(\Project\Entities\Plumber::class);
    }

    public function show(Request $request, $id) {
        return parent::show($request, $id);
    }
}

我已经尝试了所有关于加载策略和调用 authorizeResourceauthorize() 的建议,其中包含 class 名称的字符串声明,以转义命名空间和不转义命名空间。

单步执行 Xdebug 似乎在 vendor/laravel/framework/src/Illuminate/Auth/Access/Gate.php

上失败
public function authorize($ability, $arguments = [])
{
    $result = $this->raw($ability, $arguments);

    if ($result instanceof Response) {
        return $result;
    }

    return $result ? $this->allow() : $this->deny();
}

其中 $result = $this->raw($ability, $arguments); 返回 false。 $ability = view$arguments = 空数组。

我读过 https://github.com/laravel/framework/issues/22847#issuecomment-521308861 用户提到需要在方法声明中传递模型,我无法通过巨大的系统重写来做到这一点。

路由列表是

| GET|HEAD                               | api/plumber                                         | plumber.index                 | Project\Http\Controllers\PlumberController@index                     | api,auth:api,role:valuer,plumber                                   |
| POST                                   | api/plumber                                         | plumber.store                 | Project\Http\Controllers\PlumberController@store                     | api,auth:api,role:valuer,plumber,can:create,Project\Entities\plumber |
| GET|HEAD                               | api/plumber/{plumber}                               | plumber.show                  | Project\Http\Controllers\PlumberController@show                      | api,auth:api,role:valuer,plumber,can:view,plumber                  |
| PUT|PATCH                              | api/plumber/{plumber}                               | plumber.update                | Project\Http\Controllers\PlumberController@update                    | api,auth:api,role:valuer,plumber,can:update,plumber                |
| DELETE                                 | api/plumber/{plumber}                               | plumber.destroy               | Project\Http\Controllers\PlumberController@destroy   

根据我的发现,使用 authorizeResource() 是不可能的。您需要调用 $this->authorize() 并传入一个对象,因为它会在数组中出错。

class AuthServiceProvider extends ServiceProvider
{
    protected $policies = [
        Plumber::class => PlumberPolicy::class
    ];
}

authorize() 需要一个对象,如果传递一个数组会抛出错误。

class PlumberController extends ApiController
{
    public function index(Request $request) { 
        $this->authorize('viewAny', \Project\Entities\Plumber::class);
    }
    public function update(Request $request, $id) { 
        $plumber = $this->repository->getByID($id);

        $this->authorize('update', $plumber);
    }
    public function store(Request $request) { 
        $this->authorize('create', \Project\Entities\Plumber::class);
    }
    public function show(Request $request, $id) { 
        $plumber = $this->repository->getByID($id);

        $this->authorize('update', $plumber);
    }
    public function destroy(Request $request, $id) { 
        $plumber = $this->repository->getByID($id);
        
        $this->authorize('update', $plumber);
    }
}