Maison  >  Article  >  cadre php  >  Un article pour bien comprendre l'utilisation de Laravel Guards

Un article pour bien comprendre l'utilisation de Laravel Guards

藏色散人
藏色散人avant
2021-12-07 15:56:513071parcourir

Laravel possède l'une des fonctionnalités les plus courantes, qui consiste à authentifier les utilisateurs persistants, auquel cas ceux-ci sont stockés dans n'importe quelle base de données prise en charge (MySQL, SQLite, etc.), si l'on considère que vous pouvez le faire. Ce truc est incroyable lors de la configuration une application Web et authentifier les utilisateurs avec des mots de passe oubliés et tous les ajustements. Que se passe-t-il lorsque vous avez besoin de quelque chose de plus complexe ?

Par exemple, que se passe-t-il si vous avez besoin de plusieurs types d'utilisateurs avec leur propre portail d'authentification ? Ces différents types d'utilisateurs peuvent être des clients, des fournisseurs et des administrateurs. Des mécanismes comme celui-ci peuvent très vite devenir très déroutants, je le sais parce que j'y suis allé. Nous planifions souvent à l'avance la structure de la base de données, mais pas à quoi ressembleront nos processus et mécanismes d'authentification.

Recommandé : "tutoriel laravel"

Dans cet article, j'espère décomposer et expliquer comment gérer ces scénarios.

Que sont les gardes ?

Les gardes dans Laravel sont un mécanisme par lequel votre application peut savoir si quelqu'un ou même quelque chose est authentifié. Lorsque nous regardons l'installation par défaut de Laravel, nous voyons généralement un garde, qui est Web. Lorsqu'un visiteur s'authentifie via cette protection, toute utilisation du middleware d'authentification permettra à l'utilisateur de visualiser la page car la protection par défaut est toujours le Web. Si une personne navigue et n’est authentifiée à aucun moment, elle est appelée invité dans cette garde.

Généralement, lors de l'ajout d'une protection supplémentaire à une application Web, cela fournit un mécanisme d'authentification différent pour l'API, tel que des jetons utilisateur. Toutefois, cette protection par défaut ne doit pas nécessairement être la seule protection Web de votre application. En fait, nous pouvons configurer un Guard pour différents types d'utilisateurs, même ceux qui ne s'authentifient pas à l'aide de noms d'utilisateur et de mots de passe traditionnels.

Comment créer un nouveau fournisseur Authentifiable pour Eloquent ?

Pour montrer comment créer un nouveau Authenticatable, nous utiliserons un exemple de page à travers laquelle les clients des commandes peuvent s'authentifier. Les clients ne peuvent s'authentifier auprès de l'application qu'à l'aide d'une URL signée, et une fois authentifiés, ils peuvent effectuer d'autres actions, comme annuler une commande.

Tout d'abord, nous créons un nouveau modèle :

php artisan make:model Order

Maintenant, nous devons modifier le modèle Order dans app/Models/Order.php, en ajoutant quelques interfaces et traits. Cela satisfait au modèle d'ordre qui peut être utilisé avec des gardes et des types de fournisseurs Eloquent.

Order.php

<?php

namespace App\Models;

use Illuminate\Auth\Authenticatable;
use Illuminate\Auth\MustVerifyEmail;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Foundation\Auth\Access\Authorizable;

class Order extends Model implements
    AuthenticatableContract,
    AuthorizableContract
{
    use Authenticatable;
    use Authorizable;
    use MustVerifyEmail;

    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }
}

Veuillez noter que par rapport au modèle User prêt à l'emploi, nous pouvons simplement étendre la classe User du framework, mais comme nous n'allons pas utiliser de mots de passe, nous ignorerons pouvoir re- Le modèle dont le mot de passe est défini.

Une fois cela fait, nous devons ajouter notre protection à la configuration d'authentification dans configs/auth.php. Comme nous utilisons également un modèle différent, nous devons implémenter un fournisseur supplémentaire, que nous appellerons le fournisseur d'ordres, qui sera utilisé par la garde client.

auth.php

<?php
return [
    // auth.php 配置的其余部分

    &#39;guards&#39; => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'customer' => [
            'driver' => 'session',
            'provider' => 'orders',
        ],
    ],

    // auth.php 配置的其余部分
    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],

        'orders' => [
            'driver' => 'eloquent',
            'model' => App\Models\Order::class,
        ],

        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],
];

Ça y est, notre nouveau gardien est désormais authentifié, mais nous avons besoin d'un mécanisme pour authentifier les visiteurs sans exiger de mot de passe.

L'authentification nécessite-t-elle un mot de passe ?

Techniquement, oui, l'authentification nécessite un mot de passe car elle fait partie de l'interface LightmateContractsAuthAuthenticatable qui nécessite une implémentation de getAuthPassword(). Dans l’exemple précédent, nous avons utilisé le trait Authenticatable pour fournir l’implémentation. Cependant, ce code ne sera utilisé que si nous essayons d'utiliser la méthode try du guard, que nous n'utiliserons pas.

Dans ce cas, nous n'avons pas l'intention de vérifier notre commande par e-mail et mot de passe, nous n'avons donc pas à nous en soucier. Au lieu de cela, nous allons simplement créer un nouveau composant middleware qui gérera l'authentification à partir d'une URL signée que seule notre application peut générer pour que les vendeurs puissent la fournir aux clients.

Nous allons d'abord mettre en place un exemple d'itinéraire pour notre commande dans routes/web.php.

web.php

<?php

use Illuminate\Support\Facades\Route;

Route::get(&#39;order/{order}&#39;, function (\App\Models\Order $order) {
    return view(&#39;order.view&#39;, [&#39;order&#39; => $order]);
})
    ->name('order.view')
    ->middleware([
        'auth.signed:order,customer',
        'auth:customer,seller',
    ]);

Veuillez noter que nous avons ajouté un middleware authentifié. Il n'existe pas encore, nous devons donc en créer un et l'ajouter au noyau http. Nous pouvons créer un middleware en utilisant la commande suivante :

php artisan make:middleware AuthenticateWhenRequestIsSigned

这将创建app/Http/Middleware/AuthenticateWhenRequestIsSigned.php文件,我们可以编辑该文件。我们将向Handle方法添加两个参数,这两个参数将是要从路由和我们想要进行身份验证的守卫中使用的参数名称。然后,Handle方法的代码非常简单,如果请求已签名,则使用Order参数中的ID值对客户进行身份验证。

AuthenticateWhenRequestIsSigned.php

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class AuthenticateWhenRequestIsSigned
{
    public function handle(Request $request, Closure $next, string $parameterName, string $guard = null)
    {
        if ($request->hasValidSignature()) {
            if (Auth::guard($guard)->check()) {
                Auth::guard($guard)->logout();
            }
            Auth::guard($guard)->loginUsingId($request->route()->parameter($parameterName));
        }

        return $next($request);
    }
}

现在我们已经创建了中间件,我们需要在内核中注册它。

Kernel.php

<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    // Http内核的其余部分

    /**
     * 应用程序的路由中间件。
     *
     * 这些中间件可以分配到组中,也可以单独使用。
     *
     * @var array
     */
    protected $routeMiddleware = [
        // 数组的其余部分
        &#39;auth.signed&#39; => \App\Http\Middleware\AuthenticateWhenRequestIsSigned::class,
        // 数组的其余部分
    ];

    // Http内核的其余部分
}

这样做不会使中间件工作,因为我们还将身份验证中间件用于我们的路由,这意味着身份验证签名的中间件永远不会执行,因为身份验证中间件具有优先级,并且将在签名中间件有机会对客户进行身份验证之前终止请求。

要解决这个问题,我们只需要向内核添加一个额外的数组,以设置在会话启动中间件之后运行的身份验证签名中间件的优先级。

Kernel.php

<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    // HTTP内核的其余部分

    /**
     * 中间件的优先级排序列表。
     *
     * 强制非全局中间件始终处于给定顺序。
     *
     * @var string[]
     */
    protected $middlewarePriority = [
        \Illuminate\Cookie\Middleware\EncryptCookies::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\AuthenticateWhenRequestIsSigned::class,
        \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class,
        \Illuminate\Routing\Middleware\ThrottleRequests::class,
        \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class,
        \Illuminate\Session\Middleware\AuthenticateSession::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
        \Illuminate\Auth\Middleware\Authorize::class,
    ];
}

我们可以通过向内核添加midlewarePriority属性来实现这一点,覆盖父级Kernel。在AuthenticatesRequests中间件和StartSession中间件之前这样做意味着,当URL中提供有效签名时,中间件可以对客户进行身份验证。

现在,每当访问者使用带有有效签名的url登陆该页面时,他们将通过我们的守卫进行身份验证,并能够在没有签名的情况下重新访问该url,直到他们的会话超时。不过,这仍然有一个问题,任何这样做的客户也不仅能够查看他们的订单,还可以通过简单地更改URL中的id来查看任何订单。请记住,Authentication不是Authorization,这意味着为了保护客户的其他订单,我们需要添加一些授权。

我们如何保护客户只看到一个订单?

这是一个相当简单的程序。我们现在只需要一个策略,但在本例中,我们需要使用guard参数作为policy make命令的一部分。这将允许我们生成我们需要的大部分代码。

php artisan make:policy --guard customer --model App/Models/Order CustomerOrderPolicy

现在,由于模型和可验证的匹配,我们需要重命名几个方法的参数,并为这些方法分配一个返回值,这将允许订单只查看和更新它自己。我们需要继续编辑app/policies/customerOrderPolicy.php。我们实现了用于updatingviewing单个订单的方法,其余的可以返回false。

CustomerOrderPolicy.php

<?php

namespace App\Policies;

use App\Models\Order;
use Illuminate\Auth\Access\HandlesAuthorization;

class CustomerOrderPolicy
{
    use HandlesAuthorization;

    public function viewAny(Order $order)
    {
        return false;
    }

    public function view(Order $customer, Order $order)
    {
        return $customer->is($order);
    }

    public function create(Order $order)
    {
        return false;
    }

    public function update(Order $customer, Order $order)
    {
        return $customer->is($order);
    }

    public function delete(Order $customer, Order $order)
    {
        return false;
    }

    public function restore(Order $customer, Order $order)
    {
        return false;
    }

    public function forceDelete(Order $customer, Order $order)
    {
        return false;
    }
}

一旦我们完成了这一点,我们只需要注册策略并将中间件添加到路由中。现在,当经过身份验证的用户试图访问除他们自己的订单之外的任何订单时,他们都将失败。这样,我们就通过对用户的身份验证和授权保护了应用程序。

AuthServiceProvider.php

<?php

namespace App\Providers;

use App\Models\Order;
use App\Policies\CustomerOrderPolicy;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * 应用程序的策略映射.
     *
     * @var array
     */
    protected $policies = [
        Order::class => CustomerOrderPolicy::class,
    ];

    // AuthServiceProvider 的其余部分
}

现在,我们通过配置路由查看订单的策略。

web.php

<?php

use Illuminate\Support\Facades\Route;

Route::get(&#39;order/{order}&#39;, function (\App\Models\Order $order) {
    return view(&#39;order.view&#39;, [&#39;order&#39; => $order]);
})
    ->name('order.view')
    ->middleware([
        'auth.signed:order,customer',
        'auth:customer,seller',
        'can:view,order'
    ]);

将 Web Guard 重命名为更具描述性的东西有多难?

只有当我们也有一名称为卖家的守卫时,让一名称为客户的守卫才真正有意义,他们仍然会使用电子邮件和密码进行身份验证,为客户生成订单。我们已经有了 web 守卫,但这并不是真正适合所有的 web 用户,而是为卖家准备的,所以我们会相应地给它命名。

重命名默认守卫可能会变得很棘手,特别是在其他中间件和包(如Laravel Sanctum和Fortify)将按名称使用 Web 守卫的情况下。幸运的是,这两个包都有配置选项,可以轻松地更改这一点。

首先,我们必须编辑 configs/auth.php 以拥有一个名为卖家的守卫。然后,我们还需要更新默认值以反映名称更改。

auth.php

<?php
return [
    // auth.php 其余的配置部分

    &#39;guards&#39; => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'customer' => [
            'driver' => 'session',
            'provider' => 'orders',
        ],
    ],

    // auth.php 其余的配置部分

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],

        'orders' => [
            'driver' => 'eloquent',
            'model' => App\Models\Order::class,
        ],

        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],
];

如果我们还使用 Fortify 和 Sanctum 那么每个配置都需要设置一个 guard ,该值将为这些包配置保护. 之后就可以使用了. 需要用 auth:seller 替代 auth:web 更新路由 。

结论

与 Guards 一起工作一开始可能会有点混乱,在做出长期决定之前肯定需要一些探索。我曾在多个项目中工作过,在这些项目中,分离警卫既是有益的,也是一种负担。通常,处理这种情况的最佳方法是构建一个快速原型,说明如何处理某些分离。通常,在决定访问者是否可以访问网站的特定部分时,使用 Gate  是一个更好的选择。

我已经简单介绍了本文中的所有步骤,如果您希望进行更多的实验或了解此工作流的实际工作方式,您可以从 github repository 克隆设置好的代码,在演示代码中包含了一个测试,如果您想进一步进行实验,可以使用它。

Adresse originale : https://dev.to/slyfirefox/laravel-authentication-understanding-guards-and-implementing-authenticatables-2364

Adresse de traduction : https://learnku.com/laravel/t/63367

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer