30 nov. 2025·6 min de lecture

Garde de routes Vue 3 pour accès par rôle : modèles pratiques

Garde de routes Vue 3 pour accès par rôle expliquée avec des modèles pratiques : meta de route, redirections sûres, pages 401/403 conviviales et prévention des fuites de données.

Garde de routes Vue 3 pour accès par rôle : modèles pratiques

Ce que les gardes de route résolvent réellement (et ce qu’ils ne font pas)

Les gardes de route ont un seul travail : contrôler la navigation. Elles décident si quelqu’un peut entrer sur une route, et où l’envoyer si ce n’est pas possible. Cela améliore l’expérience, mais ce n’est pas la même chose que la sécurité.

Cacher un élément de menu n’est qu’un indice, pas une autorisation. Les gens peuvent toujours taper une URL, rafraîchir sur un lien profond ou ouvrir un favori. Si votre seule protection est « le bouton n’est pas visible », vous n’avez aucune protection.

Les gardes sont utiles quand vous voulez que l’app se comporte de façon cohérente tout en bloquant des pages qui ne doivent pas être affichées, comme les zones admin, les outils internes ou les portails clients basés sur les rôles.

Les gardes vous aident à :

  • Bloquer des pages avant qu’elles ne se rendent
  • Rediriger vers la connexion ou vers une page sûre par défaut
  • Afficher un écran 401/403 clair au lieu d’une vue cassée
  • Éviter des boucles de navigation accidentelles

Ce que les gardes ne peuvent pas faire, c’est protéger les données à elles seules. Si une API renvoie des données sensibles au navigateur, un utilisateur peut toujours appeler cet endpoint directement (ou inspecter les réponses dans les outils dev) même si la page est bloquée. La vraie autorisation doit aussi se faire côté serveur.

Un bon objectif est de couvrir les deux côtés : bloquer les pages et bloquer les données. Si un agent support ouvre une route réservée aux admins, la garde doit arrêter la navigation et afficher « Accès refusé ». Séparément, votre backend doit refuser les appels API réservés aux admins, pour que les données restreintes ne soient jamais retournées.

Choisir un modèle simple de rôles et permissions

Le contrôle d’accès devient compliqué quand vous commencez avec une longue liste de rôles. Commencez par un petit ensemble que les gens comprennent vraiment, puis ajoutez des permissions plus fines seulement quand vous ressentez une vraie douleur.

Une séparation pratique est :

  • Les rôles décrivent qui est quelqu’un dans votre app.
  • Les permissions décrivent ce qu’il peut faire.

Pour la plupart des outils internes, trois rôles couvrent beaucoup de cas :

  • admin : gérer les utilisateurs et les paramètres, voir toutes les données
  • support : traiter les dossiers clients et répondre, mais pas les paramètres système
  • viewer : accès en lecture seule aux écrans approuvés

Décidez tôt d’où viennent les rôles. Les claims dans le token (JWT) sont rapides pour les gardes mais peuvent devenir obsolètes tant qu’ils ne sont pas rafraîchis. Récupérer le profil utilisateur au démarrage de l’app est toujours à jour, mais vos gardes doivent attendre que cette requête soit terminée.

Séparez également clairement vos types de routes : routes publiques (ouvertes à tous), routes authentifiées (requièrent une session) et routes restreintes (requièrent un rôle ou une permission).

Définir les règles d’accès avec meta de route

La manière la plus propre d’exprimer l’accès est de le déclarer sur la route elle‑même. Vue Router vous permet d’attacher un objet meta à chaque enregistrement de route afin que vos gardes puissent le lire ensuite. Cela garde les règles proches des pages qu’elles protègent.

Choisissez une forme meta simple et tenez‑vous en à elle dans toute l’app.

const routes = [
  {
    path: "/admin",
    component: () => import("@/pages/AdminLayout.vue"),
    meta: { requiresAuth: true, roles: ["admin"] },
    children: [
      {
        path: "users",
        component: () => import("@/pages/AdminUsers.vue"),
        // inherits requiresAuth + roles from parent
      },
      {
        path: "audit",
        component: () => import("@/pages/AdminAudit.vue"),
        meta: { permissions: ["audit:read"] },
      },
    ],
  },
  {
    path: "/tickets",
    component: () => import("@/pages/Tickets.vue"),
    meta: { requiresAuth: true, permissions: ["tickets:read"], readOnly: true },
  },
]

Pour les routes imbriquées, décidez comment les règles se combinent. Dans la plupart des apps, les enfants devraient hériter des exigences du parent. Dans votre garde, vérifiez chaque enregistrement de route correspondant (pas seulement to.meta) pour que les règles parentales ne soient pas ignorées.

Un détail qui vous fera gagner du temps plus tard : distinguez entre « peut voir » et « peut éditer ». Une route peut être visible pour support et admins, mais les actions d’édition doivent être désactivées pour le support. Un drapeau readOnly: true dans meta peut piloter le comportement UI (désactiver les actions, cacher les boutons destructifs) sans prétendre que c’est de la sécurité.

Préparer l’état d’auth pour que les gardes se comportent de façon fiable

La plupart des bugs de garde viennent d’un problème : la garde s’exécute avant que l’app sache qui est l’utilisateur.

Traitez l’auth comme une petite machine à états et faites-en la source unique de vérité. Vous voulez trois états clairs :

  • unknown : l’app vient de démarrer, la session n’est pas encore vérifiée
  • logged out : la vérification de session est terminée, pas d’utilisateur valide
  • logged in : utilisateur chargé, rôles/permissions disponibles

La règle : ne lisez jamais les rôles quand l’auth est unknown. C’est ainsi que vous obtenez des flashs d’écrans protégés ou des redirections surprises vers la connexion.

Décider comment fonctionne le rafraîchissement de session

Choisissez une stratégie de rafraîchissement et conservez‑la prévisible (par exemple : lire un token, appeler un endpoint « who am I », définir l’utilisateur).

Un motif stable ressemble à ceci :

  • Au chargement de l’app, mettez l’auth à unknown et lancez une seule requête de rafraîchissement
  • Résolvez les gardes seulement après que le rafraîchissement soit terminé (ou ait expiré)
  • Mettez en cache l’utilisateur en mémoire, pas dans le meta de route
  • En cas d’échec, mettez l’auth à logged out
  • Exposez une promesse ready (ou équivalent) que les gardes peuvent attendre

Une fois cela en place, la logique de la garde reste simple : attendez que l’auth soit prête, puis décidez de l’accès.

Pas à pas : implémenter l’autorisation au niveau des routes

Créer des pages de secours conviviales
Créez rapidement des pages 401, 403 et 404 avec les outils UI d'AppMaster.
Construire l'UI

Une approche propre consiste à garder la plupart des règles dans une garde globale, et n’utiliser des gardes par-route que lorsqu’une route a vraiment une logique spécifique.

1) Ajouter une garde globale beforeEach

// router/index.js
router.beforeEach(async (to) => {
  const auth = useAuthStore()

  // Step 2: wait for auth initialization when needed
  if (!auth.ready) await auth.init()

  // Step 3: check authentication, then roles/permissions
  if (to.meta.requiresAuth && !auth.isAuthenticated) {
    return { name: 'login', query: { redirect: to.fullPath } }
  }

  const roles = to.meta.roles
  if (roles && roles.length > 0 && !roles.includes(auth.userRole)) {
    return { name: 'forbidden' } // 403
  }

  // Step 4: allow navigation
  return true
})

Ceci couvre la plupart des cas sans disperser les vérifications dans les composants.

Quand beforeEnter convient mieux

Utilisez beforeEnter quand la règle est vraiment spécifique à la route, comme « seul le propriétaire du ticket peut ouvrir cette page » et qu’elle dépend de to.params.id. Restez bref et réutilisez le même store d’auth pour que le comportement reste cohérent.

Redirections sûres sans ouvrir de brèches

Déployer dans votre cloud
Lancez sur AppMaster Cloud, AWS, Azure ou Google Cloud quand vous êtes prêt.
Déployer l'app

Les redirections peuvent discrètement annuler votre contrôle d’accès si vous les traitez comme fiables.

Le patron courant est : quand un utilisateur est déconnecté, envoyez‑le vers la page de connexion avec un paramètre returnTo. Après connexion, lisez‑le et naviguez là‑bas. Le risque est l’open redirect (envoyer des utilisateurs vers un endroit non prévu) et les boucles.

Gardez le comportement simple :

  • Les utilisateurs déconnectés vont à Login avec returnTo réglé sur le chemin courant.
  • Les utilisateurs connectés mais non autorisés vont vers une page Forbidden dédiée (pas Login).
  • N’acceptez que des valeurs returnTo internes que vous reconnaissez.
  • Ajoutez une vérification anti‑boucle pour ne jamais rediriger vers le même endroit.
const allowedReturnTo = (to) => {
  if (!to || typeof to !== 'string') return null
  if (!to.startsWith('/')) return null
  // optional: only allow known prefixes
  if (!['/app', '/admin', '/tickets'].some(p => to.startsWith(p))) return null
  return to
}

router.beforeEach((to) => {
  if (!auth.isReady) return false

  if (!auth.isLoggedIn && to.name !== 'Login') {
    return { name: 'Login', query: { returnTo: to.fullPath } }
  }

  if (auth.isLoggedIn && !canAccess(to, auth.user) && to.name !== 'Forbidden') {
    return { name: 'Forbidden' }
  }
})

Éviter de fuiter des données restreintes pendant la navigation

La fuite la plus facile vient du chargement de données avant que vous sachiez que l’utilisateur est autorisé à les voir.

Dans Vue, cela arrive souvent quand une page récupère des données dans setup() et que la garde du routeur s’exécute un instant plus tard. Même si l’utilisateur est redirigé, la réponse peut quand même arriver dans un store partagé ou clignoter à l’écran.

Une règle plus sûre : autorisez d’abord, puis chargez.

// router guard: authorize before entering the route
router.beforeEach(async (to) => {
  await auth.ready() // ensure roles are known
  const required = to.meta.requiredRole
  if (required && !auth.hasRole(required)) {
    return { name: 'forbidden' }
  }
})

Faites aussi attention aux requêtes tardives quand la navigation change rapidement. Annulez les requêtes (par exemple avec AbortController) ou ignorez les réponses tardives en vérifiant un id de requête.

Le caching est un autre piège courant. Si vous stockez « le dernier enregistrement client chargé » globalement, une réponse réservée aux admins peut être montrée plus tard à un non‑admin qui visite le même squelette d’écran. Clénez les caches par id d’utilisateur et rôle, et videz les modules sensibles à la déconnexion (ou quand les rôles changent).

Quelques habitudes évitent la plupart des fuites :

  • Ne récupérez pas de données sensibles tant que l’autorisation n’est pas confirmée.
  • Indexez les données mises en cache par utilisateur et rôle, ou gardez‑les locales à la page.
  • Annulez ou ignorez les requêtes en cours lors des changements de route.

Pages de secours conviviales : 401, 403 et not found

Prototyper vos règles de garde
Modélisez les rôles et permissions, puis testez les liens profonds et les redirections dans un seul projet.
Essayer AppMaster

Les chemins « non » comptent autant que les chemins « oui ». De bonnes pages de secours gardent les utilisateurs orientés et réduisent les demandes de support.

401 : Connexion requise (non authentifié)

Utilisez 401 quand l’utilisateur n’est pas connecté. Gardez le message simple : il doit se connecter pour continuer. Si vous supportez le retour à la page d’origine après connexion, validez le chemin de retour pour qu’il ne puisse pas pointer hors de votre app.

403 : Accès refusé (authentifié, mais non autorisé)

Utilisez 403 quand l’utilisateur est connecté mais n’a pas la permission. Restez neutre et évitez d’indiquer des détails sensibles.

Une page 403 solide a généralement un titre clair (« Accès refusé »), une phrase d’explication et une étape suivante sûre (retour au tableau de bord, contacter un admin, changer de compte si supporté).

404 : Non trouvé

Gérez le 404 séparément du 401/403. Sinon, les gens supposent qu’ils manquent de permission alors que la page n’existe tout simplement pas.

Erreurs courantes qui cassent le contrôle d’accès

La plupart des bugs d’accès viennent de simples erreurs logiques qui apparaissent sous forme de boucles de redirection, de flashs de la mauvaise page, ou d’utilisateurs bloqués.

Les coupables habituels :

  • Traiter l’UI masquée comme de la « sécurité ». Appliquez toujours les rôles dans le routeur et dans l’API.
  • Lire des rôles depuis un état obsolète après une déconnexion/connexion.
  • Rediriger des utilisateurs non autorisés vers une autre route protégée (boucle instantanée).
  • Ignorer le moment « l’auth est encore en cours de chargement » lors du rafraîchissement.
  • Confondre 401 et 403, ce qui embrouille les utilisateurs.

Un exemple réaliste : un agent support se déconnecte et un admin se connecte sur le même ordinateur partagé. Si votre garde lit un rôle en cache avant que la nouvelle session soit confirmée, vous pouvez bloquer l’admin incorrectement ou, pire, permettre brièvement un accès que vous ne devriez pas.

Liste de contrôle rapide avant la mise en production

Mettre en place la connexion correctement
Utilisez les modules d'auth AppMaster pour gérer proprement les sessions et les profils utilisateurs.
Configurer l'auth

Faites un passage court qui se concentre sur les moments où le contrôle d’accès casse habituellement : réseaux lents, sessions expirées et URLs en favoris.

  • Chaque route protégée a des exigences meta explicites.
  • Les gardes gèrent l’état de chargement d’auth sans afficher d’UI protégée.
  • Les utilisateurs non autorisés arrivent sur une page 403 claire (pas un rebond confus vers l’accueil).
  • Toute redirection « retour » est validée et ne peut pas créer de boucles.
  • Les appels API sensibles s’exécutent seulement après confirmation de l’autorisation.

Puis testez un scénario de bout en bout : ouvrez une URL protégée dans un nouvel onglet en étant déconnecté, connectez‑vous comme utilisateur basique, et confirmez que vous arrivez soit sur la page prévue (si autorisé), soit sur un 403 propre avec une étape suivante.

Exemple : accès support vs admin dans une petite web app

Connecter des services clés rapidement
Ajoutez la facturation Stripe et des notifications Telegram ou email avec des intégrations intégrées.
Ajouter des intégrations

Imaginez une app de support avec deux rôles : support et admin. Le support peut lire et répondre aux tickets. L’admin peut aussi faire cela, plus gérer la facturation et les paramètres de l’entreprise.

  • /tickets/:id est autorisé pour support et admin
  • /settings/billing est autorisé uniquement pour admin

Maintenant un moment courant : un agent support ouvre un lien profond vers /settings/billing depuis un ancien favori. La garde doit vérifier le meta de la route avant que la page ne se charge et bloquer la navigation. Comme l’utilisateur est connecté mais n’a pas le rôle, il doit atterrir sur un fallback sûr (403).

Deux messages comptent :

  • Login required (401) : « Veuillez vous connecter pour continuer. »
  • Access denied (403) : « Vous n’avez pas accès aux paramètres de facturation. »

Ce qui ne doit pas arriver : le composant de facturation ne doit pas se monter, ni les données de facturation être récupérées, même brièvement.

Les changements de rôle en cours de session sont un autre cas limite. Si quelqu’un est promu ou rétrogradé, ne vous fiez pas au menu. Revérifiez les rôles à la navigation et décidez comment gérer les pages actives : rafraîchissez l’état d’auth quand le profil change, ou détectez les changements de rôle et redirigez hors des pages qui ne sont plus autorisées.

Étapes suivantes : maintenir les règles d’accès

Une fois les gardes en place, le risque majeur est la dérive : une nouvelle route sort sans meta, un rôle est renommé, et les règles deviennent incohérentes.

Transformez vos règles en un petit plan de test que vous pouvez exécuter chaque fois que vous ajoutez une route :

  • En tant qu’invité : ouvrez les routes protégées et confirmez que vous arrivez sur la connexion sans voir de contenu partiel.
  • En tant qu’utilisateur : ouvrez une page que vous ne devriez pas accéder et confirmez que vous obtenez un 403 clair.
  • En tant qu’admin : essayez des liens profonds copiés depuis la barre d’adresse.
  • Pour chaque rôle : rafraîchissez sur une route protégée et confirmez que le résultat est stable.

Si vous voulez une sécurité supplémentaire, ajoutez une vue dev ou une sortie console qui liste les routes et leurs exigences meta, pour que les règles manquantes sautent aux yeux immédiatement.

Si vous construisez des outils internes ou des portails avec AppMaster (appmaster.io), vous pouvez appliquer la même approche : gardez les gardes concentrées sur la navigation dans l’UI Vue3, et appliquez les permissions là où la logique backend et les données résident.

Choisissez une amélioration et implémentez‑la de bout en bout : resserrer le contrôle des chargements de données, améliorer la page 403, ou verrouiller la gestion des redirections. Les petites corrections sont celles qui arrêtent la plupart des bugs d’accès en conditions réelles.

FAQ

Les gardes de route sont-ils réellement de la sécurité, ou juste de l'UX ?

Les gardes de route contrôlent la navigation, pas l'accès aux données. Ils vous aident à bloquer une page, rediriger et afficher un état 401/403 clair, mais ils ne peuvent pas empêcher quelqu'un d'appeler directement votre API. Faites toujours respecter les mêmes permissions côté serveur pour que les données restreintes ne soient jamais retournées.

Pourquoi masquer un élément de menu n’est-il pas suffisant pour l’accès par rôle ?

Parce que masquer une interface change seulement ce que quelqu'un voit, pas ce qu'il peut demander. Les utilisateurs peuvent toujours taper une URL, ouvrir des favoris ou utiliser des liens profonds. Vous avez besoin de vérifications dans le routeur pour bloquer la page, et d'une autorisation côté serveur pour bloquer les données.

Quel est un modèle simple de rôles et permissions pour commencer ?

Commencez par un petit ensemble que tout le monde comprend, puis ajoutez des permissions quand vous en ressentez le besoin. Un socle courant est admin, support et viewer, puis ajoutez des permissions comme tickets:read ou audit:read pour des actions spécifiques. Séparez « qui vous êtes » (rôle) de « ce que vous pouvez faire » (permission).

Comment devrais-je utiliser `meta` de Vue Router pour le contrôle d’accès ?

Placez les règles d’accès dans meta sur les enregistrements de route, par exemple requiresAuth, roles et permissions. Cela garde les règles proches des pages qu’elles protègent et rend votre garde globale prévisible. Pour les routes imbriquées, vérifiez chaque enregistrement correspondant afin que les exigences parentales ne soient pas ignorées.

Comment gérer les routes imbriquées pour que les enfants héritent des restrictions du parent ?

Lisez depuis to.matched et combinez les exigences de tous les enregistrements de route correspondants. Ainsi, une route enfant ne peut pas contourner accidentellement le requiresAuth ou les roles d’un parent. Décidez d’une règle de fusion claire dès le départ (généralement : les exigences du parent s’appliquent aux enfants).

Comment empêcher les boucles de redirection et les « flashes » de pages protégées au rafraîchissement ?

Parce que la garde peut s’exécuter avant que l’app sache qui est l’utilisateur. Traitez l’auth comme trois états — unknown, logged out, logged in — et n’évaluez jamais les rôles tant que l’auth est unknown. Faites attendre les gardes qu’une étape d’initialisation soit terminée (par exemple une requête « who am I ») avant de décider.

Quand devrais-je utiliser une `beforeEach` globale vs `beforeEnter` ?

Par défaut, utilisez une beforeEach globale pour des règles cohérentes comme « nécessite connexion » et « nécessite rôle/permission ». Utilisez beforeEnter uniquement quand la règle dépend réellement des params de la route (par exemple « seul le propriétaire du ticket peut ouvrir cette page »). Assurez-vous que les deux chemins utilisent la même source d'autorité d'auth.

Comment faire des redirections après connexion sans créer de failles d’open redirect ?

Traitez returnTo comme une entrée non fiable. Autorisez seulement les chemins internes que vous reconnaissez (par exemple, valeurs commençant par / et correspondant à des préfixes connus), et ajoutez une vérification anti-boucle pour ne pas rediriger vers la même route bloquée. Les utilisateurs déconnectés vont à Login ; les utilisateurs connectés mais non autorisés vont sur une page 403 dédiée.

Comment éviter de divulguer des données restreintes pendant la navigation ?

Autorisez avant de récupérer. Si une page récupère des données dans setup() et que vous redirigez un instant plus tard, la réponse peut quand même arriver dans un store ou s’afficher brièvement. Verrouillez les requêtes sensibles derrière une autorisation confirmée, et annulez ou ignorez les requêtes en cours lors des changements de route.

Quelle est la bonne utilisation de 401 vs 403 vs 404 dans une app Vue ?

Utilisez 401 quand l’utilisateur n’est pas connecté, et 403 quand il est connecté mais non autorisé. Gardez le 404 séparé pour que les utilisateurs ne pensent pas manquer de permission pour une route qui n’existe tout simplement pas. Des pages de secours claires et cohérentes réduisent la confusion et les tickets de support.

Facile à démarrer
Créer quelque chose d'incroyable

Expérimentez avec AppMaster avec un plan gratuit.
Lorsque vous serez prêt, vous pourrez choisir l'abonnement approprié.

Démarrer