Sistema avanzado de autorización de recursos en Laravel. Parte 1. Modelo, controlador

Introducción


Hola queridos Khabrovchans.
En el curso de mi trabajo en la plataforma api, pasé mucho tiempo buscando la forma correcta de autorizar las acciones de los usuarios. La tarea se estableció de la siguiente manera: crear un sistema bastante extenso de control de acceso y acciones.
Al mismo tiempo, la mayoría de ellos están en un CRUD regular, pero otras acciones del controlador deberán ser autorizadas.
Por lo tanto, es necesario crear un sistema simple y al mismo tiempo efectivo y flexible. Había muchos conos, porque en estos artículos decidí demostrar una versión algo simplificada de lo que hice.


Me gustaría agregar por separado: el material está diseñado para los programadores practicantes y será difícil de entender para un principiante. Este artículo no programará la instalación del proyecto y la configuración para conectarse a la base de datos. Todo esto lo puede encontrar fácilmente en Internet.



Parte 1. Modelo, controladores


Entonces, la tarea: hay muchos modelos, cuyo número puede aumentar y disminuir durante el desarrollo del proyecto.
Las acciones en cada una de ellas deben ser autorizadas por roles.
Hay acciones simples, como CRUD, y otras adicionales (por ejemplo, importación, exportación ).
Es necesario simplificar el trabajo del desarrollador (a su amado) tanto como sea posible para introducirles modelos y métodos adicionales.
El artículo proporciona un ejemplo para una aplicación Api, pero la solución es adecuada no solo para eso.


Modelo


Post. ( ) app.
app/Models Posts. ( , -).
. :


    php artisan make:model Models/Post --api --migration
    php artisan migrate

: , . , .



PostController CRUD
Controller.
ModelController Controller , , PostController.
PostControllerimport() export(Post $post).
, .



Laravel Illuminate\Foundation\Auth\Access\AuthorizesRequests.
App\Http\Controllers\Controller ModelController. Controller , .
authorizeResource($model).
, (Middleware) .
:


    can:viewAny,App\Models\Post
    can:view,post
    can:update,post

, , — :


  1. viewAny view apiResource
  2. , — .

:


. resourceAbilityMap() .


<?php
protected function resourceAbilityMap()
{
    return [
        'index' => 'viewAny',
        'show' => 'view',
        'create' => 'create',
        'store' => 'create',
        'edit' => 'update',
        'update' => 'update',
        'destroy' => 'delete',
    ];
}

. Laravel . , . index() , show(Post $post) . resourceMethodsWithoutModels()


<?php
protected function resourceMethodsWithoutModels()
{
    return ['index', 'create', 'store'];
}


(ModelController)

app/Http/Controllers ModelController. :


  • $guardedMethods « » => « ».
    (Policy) , , (Gate). .
  • $methodsWithoutModels .
  • getModelClass() .
    .
  • authorizeResource($this->getModelClass()) . ( (Route), )
  • resourceAbilityMap() resourceMethodsWithoutModels() , , .

<?php

namespace App\Http\Controllers;

abstract class ModelController extends Controller
{
    /** @var array 'method' => 'policy'*/
    protected $guardedMethods = [];

    protected $methodsWithoutModels = [];

    protected abstract function getModelClass(): string;

    public function __construct()
    {
        $this->authorizeResource($this->getModelClass());
    }

    protected function resourceAbilityMap()
    {
        $base = parent::resourceAbilityMap();

        return array_merge($base, $this->guardedMethods);
    }

    protected function resourceMethodsWithoutModels()
    {
        $base = parent::resourceMethodsWithoutModels();

        return array_merge($base, $this->methodsWithoutModels);
    }
}

(PostController)

:


  • getModelClass() .
  • $methodsWithoutModels « » => « » . . , =. , , .
  • $methodsWithoutModels , . import
  • — , . — .

<?php

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\Request;

class PostController extends ModelController
{
    /** @var array 'method' => 'policy'*/
    protected $guardedMethods = [
        'export' => 'export',
        'import' => 'import',
    ];

    protected $methodsWithoutModels = ['import'];

    protected function getModelClass(): string
    {
        return Post::class;
    }

    public function index()
    { /**     */ }

    public function store(Request $request)
    { /**    */ }

    public function show(Post $post)
    { /**    */ }

    public function update(Request $request, Post $post)
    { /**    */ }

    public function destroy(Post $post)
    { /**    */ }

    public function import()
    { /**     */ }

    public function export(Post $post)
    { /**    */ }
}

(Routes)


.
'routes/api.php' ( 'routes/web.php', ) .
, , .
— ( ) , Route::apiResource('posts', 'PostController'). Laravel. .
— 'auth:api'. , .


<?php

use App\Http\Controllers\PostController;
use Illuminate\Support\Facades\Route;

Route::group(['middleware' => ['auth:api']], static function () {
    Route::post('posts/import', 'PostController@import')->name('posts.import');
    Route::get('posts/{post}/export', 'PostController@export')->name('posts.export');
    Route::apiResource('posts', 'PostController');
});

. :


php artisan route:list

— ( )


+-----------------------+---------------------------+-------------------------------+
|URI                    |Action                     |Middleware                     |
+-----------------------+---------------------------+-------------------------------+
|api/posts              |...\PostController@index   |...,can:viewAny,App\Models\Post|
|api/posts              |...\PostController@store   |...,can:create,App\Models\Post |
|api/posts/import       |...\PostController@import  |...,can:import,App\Models\Post |
|api/posts/{post}       |...\PostController@show    |...,can:view,post              |
|api/posts/{post}       |...\PostController@update  |...,can:update,post            |
| api / posts / {post} | ... \ PostController @ destroy | ..., puede: eliminar, publicar |
| api / posts / {post} / export | ... \ PostController @ export | ..., puede: exportar, publicar |
+ ----------------------- + ------------------------- - + ------------------------------- +



El siguiente paso es configurar la política de enlace Gate (Gate) <->. Pero más sobre eso en la segunda parte .


All Articles