Zum Inhalt springen
React Anfänger 25 min

Geschützte Routen

Lerne, wie du Routen in React schützt. Implementiere Authentifizierung und zeige Inhalte nur für eingeloggte Benutzer.

Aktualisiert:

Geschuetzte Routen

In den meisten Apps gibt es Bereiche, die nur fuer eingeloggte Benutzer zugaenglich sein sollen – ein Dashboard, Profil-Einstellungen oder Admin-Bereiche. Geschuetzte Routen (Protected Routes) stellen sicher, dass nur berechtigte Nutzer diese Seiten sehen koennen.

Das Konzept

User besucht /dashboard

Ist der User eingeloggt?
    ↓              ↓
   Ja             Nein
    ↓              ↓
Dashboard      Login-Seite
anzeigen       (Redirect)

Auth-Context einrichten

Zuerst brauchen wir einen Context fuer die Authentifizierung:

// context/AuthContext.jsx
import { createContext, useContext, useState } from 'react';

const AuthContext = createContext();

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);

  async function login(email, password) {
    // In der Praxis: API-Aufruf
    if (email && password) {
      const userData = {
        id: 1,
        name: email.split('@')[0],
        email,
        role: email.includes('admin') ? 'admin' : 'user'
      };
      setUser(userData);
      return true;
    }
    return false;
  }

  function logout() {
    setUser(null);
  }

  return (
    <AuthContext.Provider value={{
      user,
      isLoggedIn: !!user,
      login,
      logout
    }}>
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth muss innerhalb von AuthProvider verwendet werden');
  }
  return context;
}

ProtectedRoute-Komponente

Die Kernkomponente, die Zugriff kontrolliert:

// components/ProtectedRoute.jsx
import { Navigate, useLocation } from 'react-router-dom';
import { useAuth } from '../context/AuthContext';

function ProtectedRoute({ children }) {
  const { isLoggedIn } = useAuth();
  const location = useLocation();

  if (!isLoggedIn) {
    // Merke dir, wohin der User wollte
    return <Navigate to="/login" state={{ from: location.pathname }} replace />;
  }

  return children;
}

export default ProtectedRoute;

Routen schuetzen

// App.jsx
import { Routes, Route } from 'react-router-dom';
import { AuthProvider } from './context/AuthContext';
import ProtectedRoute from './components/ProtectedRoute';
import Layout from './components/Layout';
import Home from './pages/Home';
import Login from './pages/Login';
import Dashboard from './pages/Dashboard';
import Profile from './pages/Profile';
import Settings from './pages/Settings';

function App() {
  return (
    <AuthProvider>
      <Routes>
        <Route path="/" element={<Layout />}>
          {/* Oeffentliche Routen */}
          <Route index element={<Home />} />
          <Route path="login" element={<Login />} />

          {/* Geschuetzte Routen */}
          <Route path="dashboard" element={
            <ProtectedRoute>
              <Dashboard />
            </ProtectedRoute>
          } />
          <Route path="profile" element={
            <ProtectedRoute>
              <Profile />
            </ProtectedRoute>
          } />
          <Route path="settings" element={
            <ProtectedRoute>
              <Settings />
            </ProtectedRoute>
          } />
        </Route>
      </Routes>
    </AuthProvider>
  );
}

Login-Seite mit Redirect

Nach erfolgreichem Login den User zur gewuenschten Seite weiterleiten:

// pages/Login.jsx
import { useState } from 'react';
import { useNavigate, useLocation, Link } from 'react-router-dom';
import { useAuth } from '../context/AuthContext';

function Login() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState('');
  const { login, isLoggedIn } = useAuth();
  const navigate = useNavigate();
  const location = useLocation();

  // Wohin soll nach dem Login weitergeleitet werden?
  const from = location.state?.from || '/dashboard';

  // Bereits eingeloggt? Weiterleiten
  if (isLoggedIn) {
    return <Navigate to={from} replace />;
  }

  async function handleSubmit(e) {
    e.preventDefault();
    setError('');

    const success = await login(email, password);
    if (success) {
      navigate(from, { replace: true });
    } else {
      setError('Login fehlgeschlagen. Pruefe deine Zugangsdaten.');
    }
  }

  return (
    <div style={{ maxWidth: '400px', margin: '2rem auto' }}>
      <h1>Anmelden</h1>
      {error && (
        <div style={{
          padding: '0.75rem',
          backgroundColor: '#fee2e2',
          color: '#dc2626',
          borderRadius: '8px',
          marginBottom: '1rem'
        }}>
          {error}
        </div>
      )}
      <form onSubmit={handleSubmit}>
        <div style={{ marginBottom: '1rem' }}>
          <label>E-Mail</label>
          <input
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            required
            style={{ width: '100%', padding: '8px', borderRadius: '4px', border: '1px solid #ccc' }}
          />
        </div>
        <div style={{ marginBottom: '1rem' }}>
          <label>Passwort</label>
          <input
            type="password"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
            required
            style={{ width: '100%', padding: '8px', borderRadius: '4px', border: '1px solid #ccc' }}
          />
        </div>
        <button type="submit" style={{
          width: '100%',
          padding: '10px',
          backgroundColor: '#3498db',
          color: 'white',
          border: 'none',
          borderRadius: '6px',
          cursor: 'pointer'
        }}>
          Einloggen
        </button>
      </form>
    </div>
  );
}

export default Login;

Rollenbasierter Zugriff

Manche Seiten sollen nur fuer bestimmte Rollen zugaenglich sein:

// components/RoleRoute.jsx
import { Navigate } from 'react-router-dom';
import { useAuth } from '../context/AuthContext';

function RoleRoute({ children, allowedRoles }) {
  const { user, isLoggedIn } = useAuth();

  if (!isLoggedIn) {
    return <Navigate to="/login" replace />;
  }

  if (!allowedRoles.includes(user.role)) {
    return <Navigate to="/unauthorized" replace />;
  }

  return children;
}

// Verwendung
<Route path="admin" element={
  <RoleRoute allowedRoles={['admin']}>
    <AdminPanel />
  </RoleRoute>
} />

<Route path="editor" element={
  <RoleRoute allowedRoles={['admin', 'editor']}>
    <EditorPanel />
  </RoleRoute>
} />

Zeige verschiedene Links basierend auf dem Login-Status:

function Navigation() {
  const { user, isLoggedIn, logout } = useAuth();

  return (
    <nav style={{
      display: 'flex',
      justifyContent: 'space-between',
      alignItems: 'center',
      padding: '1rem 2rem',
      backgroundColor: 'white',
      boxShadow: '0 1px 3px rgba(0,0,0,0.1)'
    }}>
      <Link to="/" style={{ fontWeight: 'bold', fontSize: '1.25rem', textDecoration: 'none' }}>
        MeineApp
      </Link>

      <div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
        <NavLink to="/">Home</NavLink>

        {isLoggedIn ? (
          <>
            <NavLink to="/dashboard">Dashboard</NavLink>
            <NavLink to="/profile">Profil</NavLink>
            {user.role === 'admin' && (
              <NavLink to="/admin">Admin</NavLink>
            )}
            <span>Hallo, {user.name}!</span>
            <button onClick={logout}>Abmelden</button>
          </>
        ) : (
          <NavLink to="/login">Anmelden</NavLink>
        )}
      </div>
    </nav>
  );
}

Lade-Zustand beruecksichtigen

In echten Apps musst du pruefen, ob die Auth-Daten bereits geladen sind:

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // Beim Start pruefen, ob ein Token vorhanden ist
    async function checkAuth() {
      const token = localStorage.getItem('token');
      if (token) {
        try {
          const response = await fetch('/api/me', {
            headers: { Authorization: `Bearer ${token}` }
          });
          if (response.ok) {
            const userData = await response.json();
            setUser(userData);
          }
        } catch (err) {
          localStorage.removeItem('token');
        }
      }
      setLoading(false);
    }

    checkAuth();
  }, []);

  if (loading) {
    return <div>App wird geladen...</div>;
  }

  return (
    <AuthContext.Provider value={{ user, isLoggedIn: !!user, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

Uebungen

  1. Implementiere ein vollstaendiges Auth-System mit Login, Logout und geschuetztem Dashboard
  2. Erstelle rollenbasierte Routen mit Admin- und User-Bereichen
  3. Baue eine “Nach Login weiterleiten”-Funktion, die den User nach dem Login dorthin bringt, wo er hinwollte

Was kommt als Naechstes?

Du beherrschst jetzt geschuetzte Routen. Im naechsten Kapitel lernst du, wie du Daten mit der Fetch API lädst – die Grundlage fuer jede App, die mit einem Backend kommuniziert.

Zusammenfassung

  • ProtectedRoute prueft den Auth-Status und leitet bei Bedarf um
  • Speichere die gewuenschte URL im state, um nach dem Login dorthin zu leiten
  • Rollenbasierte Routen kontrollieren Zugriff basierend auf Benutzerrollen
  • Passe die Navigation dynamisch an den Login-Status an
  • Beruecksichtige den Lade-Zustand beim initialen Auth-Check
  • Nutze einen Auth-Context fuer die zentrale Benutzer-Verwaltung

Pro-Tipp: Geschuetzte Routen im Frontend sind nur die halbe Miete! Sie verhindern, dass die UI angezeigt wird – aber die eigentliche Sicherheit muss immer im Backend passieren. API-Endpunkte muessen den Auth-Token validieren und Zugriffsrechte pruefen!

Zurück zum React Kurs