30. Nov. 2025·5 Min. Lesezeit

Vue 3 Routing-Guards für rollenbasierten Zugriff: praktische Muster

Vue 3 Routing-Guards für rollenbasierten Zugriff erklärt mit praktischen Mustern: Route-meta-Regeln, sichere Redirects, freundliche 401/403-Fallbacks und wie man Datenlecks vermeidet.

Vue 3 Routing-Guards für rollenbasierten Zugriff: praktische Muster

Was Route Guards wirklich lösen (und was nicht)

Route Guards haben eine klare Aufgabe: Sie kontrollieren die Navigation. Sie entscheiden, ob jemand eine Route betreten darf und wohin ihn die App schickt, wenn nicht. Das verbessert die UX, ist aber nicht dasselbe wie Sicherheit.

Ein Menüeintrag auszublenden ist nur ein Hinweis, keine Autorisierung. Leute können trotzdem eine URL eintippen, auf einen Deep Link aktualisieren oder ein Lesezeichen öffnen. Wenn dein einziger Schutz „der Button ist nicht sichtbar“ ist, hast du keinen Schutz.

Guards sind dann nützlich, wenn die App sich konsistent verhalten soll und Seiten blockiert werden müssen, die nicht angezeigt werden dürfen — etwa Admin-Bereiche, interne Tools oder rollenbasierte Kundenportale.

Guards helfen dir dabei:

  • Seiten zu blockieren, bevor sie gerendert werden
  • Zum Login oder einem sicheren Standard umzuleiten
  • Eine klare 401/403-Seite statt einer kaputten Ansicht zu zeigen
  • Unbeabsichtigte Navigationsschleifen zu vermeiden

Was Guards nicht leisten können, ist allein Daten zu schützen. Wenn eine API sensible Daten an den Browser zurückgibt, kann ein Nutzer diesen Endpunkt direkt aufrufen (oder Antworten in den DevTools inspizieren), auch wenn die Seite blockiert ist. Reale Autorisierung muss also auch auf dem Server stattfinden.

Ein gutes Ziel ist, beide Seiten abzudecken: Seiten blockieren und Daten schützen. Wenn ein Support-Mitarbeiter eine Admin-Route öffnet, sollte der Guard die Navigation stoppen und „Zugriff verweigert“ anzeigen. Separat sollte dein Backend Admin-exklusive API-Aufrufe ablehnen, sodass eingeschränkte Daten niemals zurückgegeben werden.

Wähle ein einfaches Rollen- und Berechtigungsmodell

Zugriffskontrolle wird kompliziert, wenn du mit einer langen Rollenliste startest. Beginne mit wenigen Rollen, die die Leute wirklich verstehen, und füge feinere Berechtigungen erst hinzu, wenn du echten Bedarf spürst.

Eine praktische Aufteilung ist:

  • Rollen beschreiben, wer jemand in deiner App ist.
  • Berechtigungen beschreiben, was sie tun dürfen.

Für die meisten internen Tools reichen drei Rollen weit:

  • admin: verwaltet Nutzer und Einstellungen, sieht alle Daten
  • support: bearbeitet Kundenfälle und Antworten, aber keine Systemeinstellungen
  • viewer: Lesezugriff auf freigegebene Bereiche

Entscheide früh, woher die Rollen stammen. Token-Claims (JWT) sind schnell für Guards, können aber veraltet sein, bis sie aktualisiert werden. Das Nutzerprofil beim App-Start zu holen ist immer aktuell, aber deine Guards müssen warten, bis diese Anfrage fertig ist.

Trenne außerdem klar deine Routentypen: öffentliche Routen (für alle offen), authentifizierte Routen (Session erforderlich) und eingeschränkte Routen (bestimmte Rolle oder Permission nötig).

Definiere Regeln mit Route meta

Die sauberste Art, Zugriff auszudrücken, ist, ihn direkt an der Route zu deklarieren. Vue Router erlaubt es, jedem Route-Record ein meta-Objekt zu geben, das Guards später lesen können. So bleiben Regeln nahe an den Seiten, die sie schützen.

Wähle eine einfache meta-Form und bleibe in der gesamten App dabei.

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 },
  },
]

Bei verschachtelten Routen entscheide, wie Anforderungen kombiniert werden. In den meisten Apps sollten Kinder die Anforderungen des Elternteils erben. Prüfe in deinem Guard alle matched-Route-Records (nicht nur to.meta), damit Elternregeln nicht übersprungen werden.

Ein Detail, das später Zeit spart: Unterscheide zwischen „ansehen dürfen“ und „bearbeiten dürfen“. Eine Route kann für Support und Admin sichtbar sein, aber Editieren sollte für Support deaktiviert werden. Ein readOnly: true-Flag in meta kann das UI-Verhalten steuern (Aktionen deaktivieren, destruktive Buttons ausblenden), ohne so zu tun, als wäre es Sicherheit.

Bereite den Auth-State vor, damit Guards zuverlässig arbeiten

Die meisten Guard-Bugs entstehen durch ein Problem: Der Guard läuft, bevor die App weiß, wer der Nutzer ist.

Behandle Auth wie eine kleine Zustandsmaschine und mach sie zur einzigen Wahrheit. Du willst drei klare Zustände:

  • unknown: App gerade gestartet, Session noch nicht geprüft
  • logged out: Session-Check fertig, kein gültiger Nutzer
  • logged in: Nutzer geladen, Rollen/Berechtigungen verfügbar

Die Regel: Lese Rollen niemals, während Auth unknown ist. So vermeidest du das Aufblitzen geschützter Bildschirme oder überraschende Redirects zum Login.

Entscheide, wie Session-Refresh funktioniert

Wähle eine Refresh-Strategie und halte sie vorhersehbar (z. B.: Token lesen, einen „who am I“-Endpoint aufrufen, Nutzer setzen).

Ein stabiles Muster sieht so aus:

  • Beim App-Start Auth auf unknown setzen und eine einzige Refresh-Anfrage starten
  • Guards erst auflösen, wenn der Refresh fertig ist (oder ein Timeout tritt)
  • Den Nutzer im Arbeitsspeicher cachen, nicht in Route meta
  • Bei Fehlschlag Auth auf logged out setzen
  • Ein ready-Promise (oder Ähnliches) bereitstellen, das Guards abwarten können

Sobald das steht, bleibt die Guard-Logik einfach: Auf Auth warten, dann Zugriff entscheiden.

Schritt für Schritt: Route-Level-Autorisierung implementieren

Prototype your guard rules
Model roles and permissions, then test deep links and redirects in one project.
AppMaster ausprobieren

Ein sauberer Ansatz ist, die meisten Regeln in einem globalen Guard zu halten und per-Route-Guards nur zu nutzen, wenn eine Route wirklich spezielle Logik braucht.

1) Füge einen globalen beforeEach-Guard hinzu

// 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
})

Das deckt die meisten Fälle ab, ohne Checks über Komponenten zu verteilen.

Wann beforeEnter besser passt

Verwende beforeEnter, wenn die Regel wirklich routenspezifisch ist, z. B. „nur der Ticket-Besitzer darf diese Seite öffnen“ und die Prüfung von to.params.id abhängt. Halte die Logik kurz und nutze denselben Auth-Store, damit das Verhalten konsistent bleibt.

Sichere Redirects ohne Lücken

Add real backend authorization
Design APIs and business rules in AppMaster so data is protected, not just routes.
Backend erstellen

Redirects können deine Zugriffskontrolle aushebeln, wenn du ihnen blind vertraust.

Das übliche Muster ist: Wenn ein Nutzer abgemeldet ist, schicke ihn zum Login und füge returnTo als Query-Param hinzu. Nach dem Login liest du das und navigierst dorthin. Das Risiko sind Open-Redirects (Nutzer auf unerwünschte Ziele schicken) und Loops.

Halte das Verhalten einfach:

  • Abgemeldete Nutzer gehen zum Login mit returnTo = aktueller Pfad.
  • Angemeldete, aber nicht berechtigte Nutzer gehen zu einer dedizierten Forbidden-Seite (nicht Login).
  • Erlaube nur interne returnTo-Werte, die du erkennst.
  • Füge eine Loop-Prüfung hinzu, damit du nie zur selben Stelle weiterleitest.
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' }
  }
})

Vermeide das Leaken eingeschränkter Daten während der Navigation

Die einfachste Leckquelle ist, Daten zu laden, bevor du weißt, ob der Nutzer sie sehen darf.

In Vue passiert das oft, wenn eine Seite in setup() Daten lädt und der Router-Guard kurz danach läuft. Selbst wenn der Nutzer umgeleitet wird, kann die Antwort in einem gemeinsamen Store landen oder kurz angezeigt werden.

Eine sichere Regel: erst autorisieren, dann laden.

// 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' }
  }
})

Achte auch auf späte Requests, wenn die Navigation schnell wechselt. Breche Anfragen ab (z. B. mit AbortController) oder ignoriere verspätete Antworten durch einen Request-ID-Check.

Caching ist eine weitere Falle. Wenn du z. B. „letzten geladenen Kunden-Datensatz“ global speicherst, kann eine Admin-Antwort später einem Nicht-Admin angezeigt werden, der dieselbe Seitenstruktur besucht. Schlüssle Caches nach Nutzer-ID und Rolle auf und lösche sensible Module beim Logout (oder wenn sich Rollen ändern).

Einige Gewohnheiten verhindern die meisten Leaks:

  • Lade sensible Daten erst, wenn die Autorisierung bestätigt ist.
  • Schlüssle gecachte Daten nach Nutzer und Rolle oder halte sie lokal zur Seite.
  • Breche oder ignoriere laufende Requests bei Routenwechseln.

Freundliche Fallbacks: 401, 403 und Not Found

Own your source code
Get production-ready Go and Vue3 code you can deploy or self-host.
Code generieren

Die „Nein“-Wege sind genauso wichtig wie die „Ja“-Wege. Gute Fallback-Seiten halten Nutzer orientiert und reduzieren Supportanfragen.

401: Login erforderlich (nicht authentifiziert)

Benutze 401, wenn der Nutzer nicht angemeldet ist. Halte die Botschaft einfach: Er muss sich einloggen, um fortzufahren. Falls du Rückkehr nach der Anmeldung unterstützt, validiere den Rückkehrpfad, damit er nicht außerhalb deiner App landet.

403: Zugriff verweigert (angemeldet, aber nicht erlaubt)

Benutze 403, wenn der Nutzer zwar angemeldet ist, aber die Berechtigung fehlt. Bleibe neutral und vermeide Hinweise auf sensible Details.

Eine solide 403-Seite hat meist einen klaren Titel („Access denied“), einen Satz Erklärung und einen sicheren nächsten Schritt (Zurück zum Dashboard, Admin kontaktieren, Account wechseln falls unterstützt).

404: Nicht gefunden

Behandle 404 getrennt von 401/403. Sonst denken Leute, sie hätten keine Berechtigung, obwohl die Seite einfach nicht existiert.

Häufige Fehler, die Zugriffskontrolle kaputt machen

Die meisten Fehler in der Zugriffskontrolle sind einfache Logikfehler, die sich als Redirect-Loops, Aufblitzen der falschen Seite oder feststeckende Nutzer zeigen.

Die üblichen Ursachen:

  • Versteckte UI als „Sicherheit“ behandeln. Setze Rollen immer im Router und in der API durch.
  • Rollen aus veraltetem State nach Logout/Login lesen.
  • Nicht autorisierte Nutzer zu einer weiteren geschützten Route umleiten (so entsteht sofort eine Schleife).
  • Den Moment ignorieren, in dem Auth noch lädt beim Refresh.
  • 401 und 403 verwechseln, was Nutzer verwirrt.

Ein realistisches Beispiel: Ein Support-Mitarbeiter loggt sich aus und ein Admin loggt sich am selben Rechner ein. Wenn dein Guard eine gecachte Rolle liest, bevor die neue Session bestätigt ist, kannst du fälschlich den Admin blockieren oder – noch schlimmer – kurz Zugriff erlauben, den du nicht erlauben solltest.

Kurze Checkliste vor dem Release

Deploy to your cloud
Launch to AppMaster Cloud, AWS, Azure, or Google Cloud when you are ready.
App bereitstellen

Mache einen kurzen Durchgang, der die Momente fokussiert, in denen Zugriffskontrolle meist versagt: langsame Netze, abgelaufene Sessions und gespeicherte URLs.

  • Jede geschützte Route hat explizite meta-Anforderungen.
  • Guards behandeln den Auth-Ladezustand ohne geschützte UI aufzublitzen.
  • Nicht autorisierte Nutzer landen auf einer klaren 403-Seite (nicht auf einer verwirrenden Weiterleitung zur Startseite).
  • Jede „return to“-Weiterleitung wird validiert und kann keine Loops erzeugen.
  • Sensible API-Aufrufe laufen erst nach bestätigter Autorisierung.

Teste dann ein Szenario Ende-zu-Ende: Öffne eine geschützte URL in einem neuen Tab, während du abgemeldet bist, melde dich als Basis-Nutzer an und prüfe, ob du entweder auf der Zielseite landest (falls erlaubt) oder eine saubere 403 mit einem nächsten Schritt siehst.

Beispiel: Support vs. Admin in einer kleinen Web-App

Set up login the right way
Use AppMaster auth modules to manage sessions and user profiles cleanly.
Auth einrichten

Stell dir ein Helpdesk mit zwei Rollen vor: support und admin. Support kann Tickets lesen und beantworten. Admin kann das auch, und zusätzlich Billing und Company-Settings verwalten.

  • /tickets/:id ist für support und admin erlaubt
  • /settings/billing ist nur für admin erlaubt

Ein häufiger Moment: Ein Support-Mitarbeiter öffnet ein altes Lesezeichen zu /settings/billing. Der Guard sollte die meta prüfen, bevor die Seite lädt, und die Navigation blockieren. Weil der Nutzer angemeldet, aber nicht berechtigt ist, landet er auf einem sicheren Fallback (403).

Zwei Nachrichten sind wichtig:

  • Login erforderlich (401): „Please sign in to continue.“
  • Zugriff verweigert (403): „You do not have access to Billing Settings.“

Was nicht passieren darf: Die Billing-Komponente mountet oder Billing-Daten werden auch nur kurz geladen.

Änderungen der Rolle während einer Session sind ein weiterer Randfall. Wenn jemand befördert oder degradiert wird, verlasse dich nicht auf das Menü. Prüfe Rollen bei der Navigation erneut und entscheide, wie du aktive Seiten behandelst: Aktualisiere den Auth-State, wenn sich das Profil ändert, oder erkenne Rollenwechsel und leite von Seiten weg, die nicht mehr erlaubt sind.

Nächste Schritte: Zugriffregeln wartbar halten

Wenn die Guards funktionieren, ist das größere Risiko Drift: Eine neue Route kommt ohne meta, eine Rolle wird umbenannt und Regeln werden inkonsistent.

Mach aus deinen Regeln einen kleinen Testplan, den du beim Hinzufügen einer Route durchgehst:

  • Als Gast: öffne geschützte Routen und bestätige, dass du zum Login landest, ohne Teile des Inhalts zu sehen.
  • Als User: öffne eine Seite, auf die du keinen Zugriff haben solltest, und bestätige, dass du eine klare 403 siehst.
  • Als Admin: teste Deep Links aus der Adresszeile.
  • Für jede Rolle: aktualisiere auf einer geschützten Route und bestätige, dass das Ergebnis stabil ist.

Wenn du eine zusätzliche Sicherheit willst, füge eine dev-only Ansicht oder Konsolenausgabe hinzu, die Routen und deren meta-Anforderungen auflistet, sodass fehlende Regeln sofort auffallen.

Wenn du interne Tools oder Portale mit AppMaster (appmaster.io) baust, kannst du denselben Ansatz anwenden: Halte Route Guards auf die Navigation im Vue3-UI fokussiert und erzwinge Berechtigungen dort, wo Backend-Logik und Daten liegen.

Wähle eine Verbesserung und implementiere sie Ende-zu-Ende: engere Gating-Logik für Datenabrufe, eine bessere 403-Seite oder strengere Redirect-Verarbeitung. Kleine Fixes sind die, die die meisten realen Zugriffsbugs stoppen.

FAQ

Sind Route Guards wirklich Sicherheit oder nur UX?

Route Guards kontrollieren Navigation, nicht den Datenzugriff. Sie helfen dabei, eine Seite zu blockieren, umzuleiten und einen sauberen 401/403-Zustand anzuzeigen, können aber nicht verhindern, dass jemand direkt deine API aufruft. Setze dieselben Berechtigungen auch auf dem Backend durch, sodass eingeschränkte Daten niemals zurückgegeben werden.

Warum reicht es nicht, ein Menüelement auszublenden?

Weil das Ausblenden einer UI nur ändert, was jemand sieht, nicht, was er anfragen kann. Nutzer können weiterhin eine URL eintippen, Lesezeichen öffnen oder Deep Links benutzen. Du brauchst Router-Checks, um die Seite zu blockieren, und serverseitige Autorisierung, um die Daten zu schützen.

Welches einfache Rollen- und Berechtigungsmodell ist ein guter Start?

Starte mit wenigen, leicht verständlichen Rollen und ergänze Berechtigungen nur bei Bedarf. Ein gängiges Basisset sind admin, support und viewer. Ergänze bei Bedarf spezifische Permissions wie tickets:read oder audit:read. Trenne dabei klar „wer du bist“ (Rolle) von „was du tun kannst“ (Permission).

Wie sollte ich Vue Router `meta` für Zugriffskontrollen nutzen?

Lege Zugriffsregeln in meta der Routen ab, z. B. requiresAuth, roles und permissions. So stehen die Regeln dicht bei den geschützten Seiten und dein globaler Guard bleibt vorhersehbar. Bei verschachtelten Routen prüfe alle matched-Records, damit Elterneinstellungen nicht umgangen werden.

Wie handhabe ich verschachtelte Routen, damit Kinder Eltern-Beschränkungen erben?

Lese aus to.matched und kombiniere die Anforderungen aller übereinstimmenden Route-Records. So kann eine Kindroute nicht versehentlich eine Eltern-requiresAuth oder -roles umgehen. Lege vorher fest, wie die Anforderungen zusammengeführt werden (üblich: Elternanforderungen gelten für Kinder).

Wie verhindere ich Redirect-Loops und das Aufblitzen geschützter Seiten beim Neuladen?

Oft läuft der Guard, bevor die App weiß, wer der Nutzer ist. Behandle Auth als drei Zustände—unknown, logged out, logged in—und werte Rollen niemals aus, während Auth unknown ist. Lass Guards auf eine Initialisierung (z. B. einen einmaligen „who am I“-Request) warten, bevor sie entscheiden.

Wann sollte ich `beforeEach` statt `beforeEnter` verwenden?

Nutze einen globalen beforeEach für konsistente Regeln wie „Login erforderlich“ oder „Rolle/Berechtigung nötig“. Verwende beforeEnter nur, wenn die Regel wirklich routenspezifisch ist und von Parametern abhängt (z. B. „nur der Besitzer des Tickets darf diese Seite öffnen“). Beide Wege sollten dieselbe Auth-Quelle nutzen.

Wie mache ich Post-Login-Redirects ohne Open-Redirect-Lücken?

Behandle returnTo als untrusted input. Erlaube nur interne Pfade, die du erkennst (z. B. Werte, die mit / beginnen und bekannten Präfixen entsprechen), und baue eine Loop-Prüfung ein, damit du nicht zurück auf dieselbe gesperrte Route leitest. Abgemeldete Nutzer gehen zu Login; angemeldete, aber nicht berechtigte Nutzer zu einer dedizierten 403-Seite.

Wie vermeide ich, dass eingeschränkte Daten während der Navigation geleakt werden?

Autorisieren bevor du Daten lädst. Wenn eine Seite in setup() Daten anfordert und du kurz danach umleitest, kann die Antwort dennoch in einen Store landen oder kurz sichtbar werden. Schütze sensitive Requests hinter bestätigter Autorisierung und breche oder ignoriere laufende Requests bei Routenwechseln.

Wie nutze ich 401 vs 403 vs 404 korrekt in einer Vue-App?

Verwende 401, wenn der Nutzer nicht angemeldet ist; 403, wenn der Nutzer zwar angemeldet ist, aber keine Berechtigung hat. Halte 404 separat, damit Nutzer nicht denken, sie hätten keine Berechtigung für eine Seite, die einfach nicht existiert. Klare, konsistente Fallbacks reduzieren Verwirrung und Supportanfragen.

Einfach zu starten
Erschaffe etwas Erstaunliches

Experimentieren Sie mit AppMaster mit kostenlosem Plan.
Wenn Sie fertig sind, können Sie das richtige Abonnement auswählen.

Starten