Présentation de la notion de Middleware

Nous allons évoquer ici le Middleware dans Laravel. Pourquoi est-ce si important ? Que permet-il ? Derrière ce nom barbare, on trouve un composant indispensable pour sécuriser les accès de son application.

Définition

Le middleware est une brique logicielle qui permet aux applications d'intéragir entre elles. Plus concrêtement, le middleware va intercepter les échanges, filtrer les requêtes HTTP au niveau de votre application.

On en a une illustration parfaite ici : https://mattstauffer.co/blog/laravel-5.0-middleware-filter-style#what-is-middleware

Prenons par exemple l'authentification. Si l'utilisateur tente d'accéder à une ressource protégée et qu'il n'est pas authentifié, le middleware redirigera l'utilisateur vers la page de login. Dans le cas contraire il autorisera l'accès à la ressource demandé.

Laravel inclut de base plusieurs middleware :

  • un middleware pour la maintenance
  • un middleware CSRF (Cross-Site Request Forgery) pour la protection contre les vulnérabilités des services d'authentification
  • un middleware d'authentification

Ils sont situés dans app/Http/Middleware.

Création d'un middleware

En ligne de commande :


$ php artisan make:middleware NomMiddleware

Exemple 1 :


<?php

namespace App\Http\Middleware;
use Closure;

class Admin
{
 /**
 * Handle an incoming request.
 * If the user is not an admin, we redirect him to the extranet homepage
 * We could also redirect him to another place
 * Please, do not forget to declare this class in the app/Http/Kernel.php file!
 *
 * @param \Illuminate\Http\Request $request
 * @param \Closure $next
 * @return mixed
 */
 public function handle($request, Closure $next)
 {
  if ($request->user()->admin)
  {
   return $next($request);
  }

  return new RedirectResponse(url('/'));
 }
}

Exemple 2 :


<?php

 namespace App\Http\Middleware;
 use Closure;

/* Si l'âge est inférieur à 18, on redirige l'utilisateur */
class NomMiddleware
{
 /**
 * Run the request filter.
 *
 * @param \Illuminate\Http\Request $request
 * @param \Closure $next
 * @return mixed
 */
 public function handle($request, Closure $next)
 {
  if ($request->input('age') < 18) {
   return redirect('home');
  }
 return $next($request);

}

Quelques explications

$request est la requête HTTP tandis que closure est une fonction anonyme qui conduit la requête au delà du middleware. On termine généralement par "return $next($request);" pour permettre l'accès de l'utilisateur à la ressource demandée si la condition est vérifiée.

Avant ou après

Le middleware peut intervenir avant le traitement de la requête ou bien après.

Tout dépendra si l'on retourne directement "return $next($request);" ou bien si l'on récupère le traitement de l'application dans un objet intermédiaire :


<?php

namespace App\Http\Middleware;
use Closure;

class AfterMiddleware
{

 public function handle($request, Closure $next)
 {

  $response = $next($request);

  // Perform action
  return $response;
 }
}

Assignation des middlewares

 Pour un middleware global qui impacte toutes les requêtes, le déclare dans le fichier app/Http/Kernel.php
C'est le tableau protected $middleware = []

En revanche, pour un middleware assigné à une route particulière, on le déclare préalablement dans le tableau protected $routeMiddleware = []


protected $routeMiddleware = [
    'auth' => \App\Http\Middleware\Authenticate::class,
    'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
    'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
];

Puis, dans la route, on déclare le middleware :


Route::get('admin/profile', ['middleware' => 'auth', function () {
    //
}]);

Ou plusieurs :


Route::get('/', ['middleware' => ['first', 'second'], function () {
    //
}]);

Lorque l'on chaîne les middleware, on doit le faire dans l'ordre. Ici par exemple :


Route::get('admin/pays', 'Admin\PaysController@index')->middleware(['auth', 'admin']);

On vérifie d'abord que l'utilisateur est loggué. Ensuite, on vérifie si il est admin.

Autres exemples :


// Single route
$router->get("/awesome/sauce", "AwesomeController@sauce", ['middleware' => 'auth']);

// Route group
$router->group(['middleware' => 'auth'], function() {
    // lots of routes that require auth middleware
});

Dans l'exemple précédent, on vient de voir comment on pouvait gagner du temps en groupant des routes qui partagent en commun le même middleware. Il s'agit par exemple d'une interface d'administrateur. Notez qu'il est possible d'utiliser $this->middleware dans le contructeur de votre contrôleur mais je ne conseille personnellement pas cette pratique car elle pollue votre code. Autant tout mettre au même endroit plutôt que de disséminer le code.

Paramètres du middleware

Pour savoir par exemple si un utilisateur authentifié à un rôle particulier, on peut créer un "RoleMiddleware" spécialisé. Il reçoit le type de rôle en argument.


<?php

namespace App\Http\Middleware;
use Closure;

class RoleMiddleware
{
 /**
 * Run the request filter.
 *
 * @param \Illuminate\Http\Request $request
 * @param \Closure $next
 * @param string $role
 * @return mixed
 */
 public function handle($request, Closure $next, $role)
 {
  if (! $request->user()->hasRole($role)) {
  // Redirect...
 }

 return $next($request);
}

On rajoute ensuite notre route dans le fichier App/Http/routes.php. On sépare ici le nom du middleware "retrouvé automatiquement" avec le paramètre passé au niveau de la méthode handle.

L'utilisation d'un middleware dans un contrôleur

Comme je l'ai rappelé plus haut, je ne suis pas adepte de la déclaration des middlewares dans les contrôleur. Néanmoins, je vais vous montrer quelques exemples pour votre culture :) Le mot clé "only" permet de spécifier une méthode du contrôleur impactée par le comportement du Middleware.


/**
 * @Middleware("auth.basic")
 */
public function index() {}

On peut également utiliser $this->middleware() (pourvu que notre contrôleur étant le contrôleur de base

use Illuminate\Routing\Controller;

class AwesomeController extends Controller {

    public function __construct()
    {
        $this->middleware('csrf');
        $this->middleware('auth', ['only' => 'update'])
    }

}

Conclusion

Voici une présentation assez courte de la notion de Middleware dans Laravel. Néanmoins, si ceci était encore étranger pour vous, vous avez désormais de bonnes notions pour continuer votre apprentissage de ce framework et vous en savez assez je pense, pour vous amuser :)

Je vous recommande la lecture de la documentation officielle - le lien est ci-dessous !

Sources :