Security - 3. Authorization (not finished)
Einführung
Zusätzlich zur Bereitstellung von Authentifizierungsdiensten bietet Laravel auch eine einfache Möglichkeit, Benutzeraktionen gegen eine bestimmte Ressource zu autorisieren. Wie bei der Authentifizierung ist der Ansatz von Laravel für die Autorisierung einfach, und es gibt zwei primäre Möglichkeiten, Aktionen zu autorisieren: Gates und Policies.
Denken Sie an Gates und Policies wie Routen und Controller. Gates bieten einen einfachen, auf der Schließung basierenden Ansatz für die Autorisierung, während Richtlinien, wie z.B. Controller, ihre Logik um ein bestimmtes Modell oder eine bestimmte Ressource gruppieren. Wir werden zunächst Gates und dann Richtlinien untersuchen.
Sie müssen sich beim Erstellen einer Anwendung nicht zwischen der ausschließlichen Verwendung von Gates oder der ausschließlichen Verwendung von Richtlinien entscheiden. Die meisten Anwendungen werden höchstwahrscheinlich eine Mischung aus Gates und Policies enthalten, und das ist völlig in Ordnung! Gates eignen sich am besten für Aktionen, die sich nicht auf ein Modell oder eine Ressource beziehen, wie z.B. die Anzeige eines Administrator-Dashboards. Im Gegensatz dazu sollten Richtlinien verwendet werden, wenn Sie eine Aktion für ein bestimmtes Modell oder eine Ressource autorisieren möchten.
more themes about Security [TaskID] - Click for open
Gates
Writing Gates
Gates sind Closures, die bestimmen, ob ein Benutzer zur Ausführung einer bestimmten Aktion berechtigt ist, und werden normalerweise in der Klasse App\Providers\AuthServiceProvider unter Verwendung der Gate-Fassade definiert. Gates erhalten immer eine Benutzerinstanz als erstes Argument und können optional zusätzliche Argumente wie z.B. ein relevantes Eloquent-Modell erhalten:
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Gate::define('edit-settings', function ($user) {
return $user->isAdmin;
});
Gate::define('update-post', function ($user, $post) {
return $user->id == $post->user_id;
});
}
Gates können auch wie Controller mit einer Callback-Zeichenkette im Class@method-Stil definiert werden:
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Gate::define('update-post', 'App\Policies\PostPolicy@update');
}
Authorizing Actions
Um eine Aktion über Gates zu autorisieren, sollten Sie die Methoden allows oder denies verwenden. Beachten Sie, dass Sie nicht verpflichtet sind, den aktuell authentifizierten Benutzer an diese Methoden zu übergeben. Laravel wird sich automatisch darum kümmern, den Benutzer in das Gate Closure zu leiten:
if (Gate::allows('edit-settings')) {
// The current user can edit settings
}
if (Gate::allows('update-post', $post)) {
// The current user can update the post...
}
if (Gate::denies('update-post', $post)) {
// The current user can't update the post...
}
Wenn Sie feststellen möchten, ob ein bestimmter Benutzer zur Ausführung einer Aktion berechtigt ist, können Sie die forUser-Methode an der Gate-Fassade verwenden:
if (Gate::forUser($user)->allows('update-post', $post)) {
// The user can update the post...
}
if (Gate::forUser($user)->denies('update-post', $post)) {
// The user can't update the post...
}
Sie können mehrere Aktionen gleichzeitig mit any- oder none-Methode autorisieren:
if (Gate::any(['update-post', 'delete-post'], $post)) {
// The user can update or delete the post
}
if (Gate::none(['update-post', 'delete-post'], $post)) {
// The user cannot update or delete the post
}
Intercepting Gate Checks
Manchmal möchten Sie vielleicht einem bestimmten Benutzer alle Fähigkeiten gewähren. Sie können die before-Methode verwenden, um einen Callback zu definieren, der vor allen anderen Berechtigungsprüfungen ausgeführt wird:
Gate::before(function ($user, $ability) {
if ($user->isSuperAdmin()) {
return true;
}
});
Wenn der before-Callback ein Nicht-Null-Ergebnis zurückgibt, wird dieses Ergebnis als Ergebnis der Prüfung angesehen.
Sie können die after-Methode verwenden, um einen Callback zu definieren, der nach allen anderen Berechtigungsprüfungen ausgeführt wird:
Gate::after(function ($user, $ability, $result, $arguments) {
if ($user->isSuperAdmin()) {
return true;
}
});
Ähnlich wie bei der before-Prüfung wird, wenn der after-Callback ein Nicht-Null-Ergebnis zurückgibt, dieses Ergebnis als das Ergebnis der Prüfung angesehen.
Creating Policies
Generating Policies
Policies sind Klassen, die die Berechtigungslogik um ein bestimmtes Modell oder eine bestimmte Ressource herum organisieren. Wenn Ihre Anwendung beispielsweise ein Blog ist, verfügen Sie möglicherweise über ein Post-Modell und eine entsprechende PostPolicy, um Benutzeraktionen wie das Erstellen oder Aktualisieren von Posts zu autorisieren.
Sie können eine Richtlinie mit dem Befehl make:policy artisan erstellen. Die generierte Richtlinie wird im Verzeichnis app/Policies abgelegt. Wenn dieses Verzeichnis in Ihrer Anwendung nicht existiert, wird Laravel es für Sie erstellen:
php artisan make:policy PostPolicy
Der Befehl make:policy erzeugt eine leere Policy-Klasse. Wenn Sie eine Klasse mit den grundlegenden "CRUD"-Richtlinienmethoden erzeugen möchten, die bereits in der Klasse enthalten sind, können Sie bei der Ausführung des Befehls ein --model angeben:
php artisan make:policy PostPolicy --model=Post
Registering Policies
Sobald die Richtlinie existiert, muss sie registriert werden. Der AuthServiceProvider, der mit neuen Laravel-Anwendungen geliefert wird, enthält eine Policy-Eigenschaft, die Ihre Eloquent-Modelle den entsprechenden Policies zuordnet. Die Registrierung einer Richtlinie weist Laravel an, welche Richtlinie bei der Autorisierung von Aktionen gegen ein bestimmtes Modell verwendet werden soll:
<?php
namespace App\Providers;
use App\Post;
use App\Policies\PostPolicy;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
Post::class => PostPolicy::class,
];
/**
* Register any application authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
//
}
}
Policy Auto-Discovery
Anstatt Modellrichtlinien manuell zu registrieren, kann Laravel Richtlinien automatisch ermitteln, solange das Modell und die Richtlinie den Standard-Laravel-Namenskonventionen folgen. Insbesondere müssen sich die Richtlinien in einem Richtlinienverzeichnis unterhalb des Verzeichnisses befinden, das die Modelle enthält. So können beispielsweise die Modelle im Verzeichnis app platziert werden, während die Richtlinien im Verzeichnis app/Policies platziert werden können. Darüber hinaus muss der Richtlinienname mit dem Modellnamen übereinstimmen und ein Policy-Suffix haben. Ein User-Modell würde also einer UserPolicy-Klasse entsprechen.
Wenn Sie Ihre eigene Logik zur Richtlinienermittlung bereitstellen möchten, können Sie einen benutzerdefinierten Rückruf mit der Methode Gate::guessPolicyNamesUsing registrieren. Normalerweise sollte diese Methode von der Boot-Methode des AuthServiceProviders Ihrer Anwendung aufgerufen werden:
use Illuminate\Support\Facades\Gate;
Gate::guessPolicyNamesUsing(function ($modelClass) {
// return policy class name...
});
Writing Policies
Policy Methods
Sobald die Richtlinie registriert wurde, können Sie Methoden für jede Aktion, die sie autorisiert, hinzufügen. Definieren wir zum Beispiel eine Update-Methode in unserer PostPolicy, die bestimmt, ob ein bestimmter User eine bestimmte Post-Instanz aktualisieren kann.
Die Update-Methode erhält einen User und eine Post-Instanz als Argumente und sollte true oder false zurückgeben, was anzeigt, ob der User berechtigt ist, den gegebenen Post zu aktualisieren. Lassen Sie uns also für dieses Beispiel überprüfen, ob die Benutzer-ID mit der user_id des Posts übereinstimmt:
<?php
namespace App\Policies;
use App\User;
use App\Post;
class PostPolicy
{
/**
* Determine if the given post can be updated by the user.
*
* @param \App\User $user
* @param \App\Post $post
* @return bool
*/
public function update(User $user, Post $post)
{
return $user->id === $post->user_id;
}
}
Sie können weiterhin zusätzliche Methoden auf der Richtlinie definieren, wenn dies für die verschiedenen von ihr autorisierten Aktionen erforderlich ist. Sie können beispielsweise Methoden zum view oder delete definieren, um verschiedene Post-Aktionen zu autorisieren, aber denken Sie daran, dass es Ihnen freisteht, Ihren Policy-Methoden einen beliebigen Namen zu geben.
Methods Without Models
Einige Richtlinienmethoden erhalten nur den aktuell authentifizierten Benutzer und nicht eine Instanz des von ihnen autorisierten Modells. Diese Situation tritt am häufigsten bei der Autorisierung von Create-Aktionen auf. Wenn Sie zum Beispiel einen Blog erstellen, möchten Sie vielleicht überprüfen, ob ein Benutzer überhaupt berechtigt ist, Beiträge zu erstellen.
Bei der Definition von Richtlinienmethoden, die keine Modellinstanz erhalten, wie z.B. eine Create-Methode, erhält diese keine Modellinstanz. Stattdessen sollten Sie die Methode so definieren, dass nur der authentifizierte Benutzer erwartet wird:
/**
* Determine if the given user can create posts.
*
* @param \App\User $user
* @return bool
*/
public function create(User $user)
{
//
}
Guest Users
Standardmäßig geben alle Gates und Richtlinien automatisch false zurück, wenn die eingehende HTTP-Anfrage nicht von einem authentifizierten Benutzer initiiert wurde. Sie können diese Berechtigungsprüfungen jedoch zu Ihren Gates und Richtlinien durchgehen lassen, indem Sie einen "optionalen" Typ-Hinweis deklarieren oder einen Standardwert null für die Benutzerargumentdefinition angeben:
<?php
namespace App\Policies;
use App\User;
use App\Post;
class PostPolicy
{
/**
* Determine if the given post can be updated by the user.
*
* @param \App\User $user
* @param \App\Post $post
* @return bool
*/
public function update(?User $user, Post $post)
{
return $user->id === $post->user_id;
}
}
Policy Filters
Für bestimmte Benutzer möchten Sie möglicherweise alle Aktionen innerhalb einer bestimmten Richtlinie autorisieren. Um dies zu erreichen, definieren Sie eine before-Methode auf der Richtlinie. Die before-Methode wird vor allen anderen Methoden in der Richtlinie ausgeführt, so dass Sie die Möglichkeit haben, die Aktion zu autorisieren, bevor die beabsichtigte Methode der Richtlinie tatsächlich aufgerufen wird. Diese Funktion wird am häufigsten für die Autorisierung von Anwendungsadministratoren verwendet, die jede beliebige Aktion ausführen können:
public function before($user, $ability)
{
if ($user->isSuperAdmin()) {
return true;
}
}
Wenn Sie alle Berechtigungen für einen Benutzer verweigern möchten, sollten Sie von der vorhergehenden Methode false zurückgeben. Wenn null zurückgegeben wird, fällt die Berechtigung an die Richtlinienmethode durch.
Authorizing Actions Using Policies
Via The User Model
Das User-Modell, das Ihrer Laravel-Anwendung beiliegt, enthält zwei hilfreiche Methoden zur Autorisierung von Aktionen: can und cant. Die Dosenmethode erhält die Aktion, die Sie autorisieren möchten, und das entsprechende Modell. Lassen Sie uns zum Beispiel feststellen, ob ein Benutzer berechtigt ist, ein bestimmtes Post-Modell zu aktualisieren:
if ($user->can('update', $post)) {
//
}
Wenn eine Richtlinie für das gegebene Modell registriert ist, ruft die can-Methode automatisch die entsprechende Richtlinie auf und gibt das boolesche Ergebnis zurück. Wenn keine Richtlinie für das Modell registriert ist, versucht die can-Methode, das auf dem Abschluss basierende Gate aufzurufen, das dem gegebenen Aktionsnamen entspricht.
Actions That Don't Require Models
Denken Sie daran, dass für einige Aktionen wie z.B. das Erstellen von Dokumenten keine Modellinstanz erforderlich ist. In diesen Situationen können Sie der can-Methode einen Klassennamen übergeben. Der Klassenname wird verwendet, um zu bestimmen, welche Richtlinie bei der Autorisierung der Aktion verwendet werden soll:
use App\Post;
if ($user->can('create', Post::class)) {
// Executes the "create" method on the relevant policy...
}
Via Middleware
Laravel enthält eine Middleware, die Aktionen autorisieren kann, bevor die eingehende Anfrage überhaupt Ihre Routen oder Controller erreicht. Standardmäßig wird der Illuminate\Auth\Middleware\Authorize-Middleware der Key can in Ihrer Klasse App\Http\Kernel zugewiesen. Betrachten wir ein Beispiel für die Verwendung der can-Middleware, um zu autorisieren, dass ein Benutzer einen Blog-Beitrag aktualisieren kann:
use App\Post;
Route::put('/post/{post}', function (Post $post) {
// The current user may update the post...
})->middleware('can:update,post');
In diesem Beispiel übergeben wir der can-Middleware zwei Argumente. Der erste ist der Name der Aktion, die wir autorisieren möchten, und das zweite ist der Routenparameter, den wir an die Richtlinienmethode übergeben möchten. Da wir in diesem Fall eine implizite Modellbindung verwenden, wird ein Post-Modell an die Richtlinienmethode übergeben. Wenn der Benutzer nicht berechtigt ist, die gegebene Aktion auszuführen, wird von der Middleware eine HTTP-Antwort mit einem 403-Statuscode generiert.
Actions That Don't Require Models
Auch hier gilt, dass für einige Aktionen, wie z.B. das create, keine Modellinstanz erforderlich ist. In diesen Situationen können Sie einen Klassennamen an die Middleware übergeben. Der Klassenname wird verwendet, um zu bestimmen, welche Richtlinie bei der Autorisierung der Aktion verwendet werden soll:
Route::post('/post', function () {
// The current user may create posts...
})->middleware('can:create,App\Post');
Via Controller Helpers
Zusätzlich zu den hilfreichen Methoden, die dem user-Modell zur Verfügung gestellt werden, bietet Laravel eine hilfreiche Autorisierungsmethode für jeden Ihrer Controller, die die Basisklasse der App\Http\Controller\Controller erweitern. Wie die can-Methode akzeptiert diese Methode den Namen der Aktion, die Sie autorisieren möchten, und das entsprechende Modell. Wenn die Aktion nicht autorisiert ist, wirft die Autorisierungsmethode eine Illuminate\Auth\Access\AuthorizationException, die der Standard-Laravel-Exception-Handler in eine HTTP-Antwort mit einem 403-Statuscode konvertiert:
<?php
namespace App\Http\Controllers;
use App\Post;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class PostController extends Controller
{
/**
* Update the given blog post.
*
* @param Request $request
* @param Post $post
* @return Response
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function update(Request $request, Post $post)
{
$this->authorize('update', $post);
// The current user can update the blog post...
}
}
Actions That Don't Require Models
Wie bereits erwähnt, erfordern einige Aktionen wie z.B. das Create von Dokumenten keine Modellinstanz. In diesen Situationen sollten Sie der authorize-Methode einen Klassennamen übergeben. Der Klassenname wird verwendet, um zu bestimmen, welche Richtlinie bei der Autorisierung der Aktion verwendet werden soll:
/**
* Create a new blog post.
*
* @param Request $request
* @return Response
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function create(Request $request)
{
$this->authorize('create', Post::class);
// The current user can create blog posts...
}
Authorizing Resource Controllers
Wenn Sie Ressourcen-Controller verwenden, können Sie die authorizeResource-Methode im Konstruktor des Controllers verwenden. Diese Methode hängt die entsprechenden Can-Middleware-Definitionen an die Methoden des Ressourcen-Controllers an.
Die authorizeResource-Methode akzeptiert den Klassennamen des Modells als erstes Argument und den Namen des Leitweg-/Anforderungsparameters, der die ID des Modells enthält, als zweites Argument:
<?php
namespace App\Http\Controllers;
use App\Post;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class PostController extends Controller
{
public function __construct()
{
$this->authorizeResource(Post::class, 'post');
}
}
Die folgenden Controller-Methoden werden auf ihre entsprechende Richtlinienmethode abgebildet:
Controller Method | Policy Method |
---|---|
show | view |
create | create |
store | create |
edit | update |
update | update |
destroy | delete |
Via Blade Templates
Wenn Sie Blade-Vorlagen schreiben, möchten Sie möglicherweise einen Teil der Seite nur dann anzeigen, wenn der Benutzer zur Ausführung einer bestimmten Aktion berechtigt ist. Sie möchten z.B. ein Aktualisierungsformular für einen Blog-Beitrag nur dann anzeigen, wenn der Benutzer den Beitrag tatsächlich aktualisieren kann. In dieser Situation können Sie die @can- und @cannot-Richtlinienfamilie verwenden:
@can('update', $post)
<!-- The Current User Can Update The Post -->
@elsecan('create', App\Post::class)
<!-- The Current User Can Create New Post -->
@endcan
@cannot('update', $post)
<!-- The Current User Can't Update The Post -->
@elsecannot('create', App\Post::class)
<!-- The Current User Can't Create New Post -->
@endcannot
Diese Direktiven sind bequeme Abkürzungen für das Schreiben von @if- und @unless-Anweisungen. Die obigen @can- und @cannot-Anweisungen lassen sich jeweils in die folgenden Anweisungen übersetzen:
@if (Auth::user()->can('update', $post))
<!-- The Current User Can Update The Post -->
@endif
@unless (Auth::user()->can('update', $post))
<!-- The Current User Can't Update The Post -->
@endunless
Sie können auch bestimmen, ob ein Benutzer eine Berechtigungsfähigkeit aus einer bestimmten Liste von Fähigkeiten hat. Verwenden Sie dazu die @canany-Direktive:
@canany(['update', 'view', 'delete'], $post)
// The current user can update, view, or delete the post
@elsecanany(['create'], \App\Post::class)
// The current user can create a post
@endcanany
Actions That Don't Require Models
Wie bei den meisten anderen Berechtigungsmethoden können Sie einen Klassennamen an die Anweisungen @can und @cannot übergeben, wenn die Aktion keine Modellinstanz erfordert:
@can('create', App\Post::class)
<!-- The Current User Can Create Posts -->
@endcan
@cannot('create', App\Post::class)
<!-- The Current User Can't Create Posts -->
@endcannot