You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React+React Router应用中基于JWT实现路由认证权限控制?

Hey there! Let's get that mock auth swapped out for a real JWT-based system in your React + React Router app with Express. I'll break this down into clear, actionable steps so you can implement it without confusion.


1. Backend (Express) Setup

First, let's handle the JWT generation and validation on your Express server.

Install Dependencies

Start by installing the required packages:

npm install jsonwebtoken bcryptjs cors
  • jsonwebtoken: For creating and verifying JWT tokens
  • bcryptjs: For securely hashing and comparing user passwords
  • cors: To handle cross-origin requests between your React frontend and Express backend

User Login Endpoint

Create a login route that validates user credentials and returns a JWT token:

const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const cors = require('cors');
const User = require('./models/User'); // Assume you have a Mongoose/Sequelize User model

// Configure CORS (adjust origin to match your React app's URL)
app.use(cors({ origin: 'http://localhost:3000', credentials: true }));

// Login route
app.post('/api/login', async (req, res) => {
  try {
    const { email, password } = req.body;
    const user = await User.findOne({ email });

    // Check if user exists
    if (!user) {
      return res.status(401).json({ message: 'Invalid email or password' });
    }

    // Verify password
    const isPasswordValid = await bcrypt.compare(password, user.password);
    if (!isPasswordValid) {
      return res.status(401).json({ message: 'Invalid email or password' });
    }

    // Generate JWT token (expires in 1 hour for security)
    const token = jwt.sign(
      { userId: user._id, email: user.email },
      process.env.JWT_SECRET, // Store this in a .env file, NEVER hardcode!
      { expiresIn: '1h' }
    );

    res.json({ token, user: { id: user._id, email: user.email } });
  } catch (err) {
    console.error(err);
    res.status(500).json({ message: 'Server error occurred' });
  }
});

JWT Validation Middleware

Create a middleware to protect your authenticated API routes:

const authenticateToken = (req, res, next) => {
  const authHeader = req.headers['authorization'];
  // Token is sent in the format: "Bearer <token>"
  const token = authHeader && authHeader.split(' ')[1];

  if (!token) {
    return res.status(401).json({ message: 'No authentication token provided' });
  }

  jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
    if (err) {
      return res.status(403).json({ message: 'Invalid or expired token' });
    }
    // Attach user data to the request for use in protected routes
    req.user = user;
    next();
  });
};

// Example protected route
app.get('/api/user/profile', authenticateToken, (req, res) => {
  res.json({ user: req.user, profileData: 'Your protected profile data' });
});

2. Frontend (React + React Router) Setup

Now let's implement the auth logic in your React app to protect routes and manage user state.

Install Dependencies

npm install react-router-dom axios

Create an Auth Context

This will manage global user state, login/logout functions, and token persistence:

import { createContext, useContext, useState, useEffect } from 'react';
import axios from 'axios';

const AuthContext = createContext();

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

  // Check for stored token on app load
  useEffect(() => {
    const token = localStorage.getItem('jwtToken');
    if (token) {
      // Verify token is still valid by hitting a protected endpoint
      axios.get('/api/user/profile', {
        headers: { Authorization: `Bearer ${token}` }
      })
      .then(res => setUser(res.data.user))
      .catch(() => localStorage.removeItem('jwtToken')) // Clear invalid token
      .finally(() => setLoading(false));
    } else {
      setLoading(false);
    }
  }, []);

  // Login function
  const login = async (email, password) => {
    try {
      const res = await axios.post('/api/login', { email, password });
      const { token, user } = res.data;
      localStorage.setItem('jwtToken', token);
      setUser(user);
      return { success: true };
    } catch (err) {
      return { success: false, message: err.response.data.message };
    }
  };

  // Logout function
  const logout = () => {
    localStorage.removeItem('jwtToken');
    setUser(null);
  };

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

// Custom hook to access auth context
export const useAuth = () => useContext(AuthContext);

Create a Protected Route Component

This component will guard routes that require authentication:

import { Navigate, Outlet } from 'react-router-dom';
import { useAuth } from './AuthContext';

export const ProtectedRoute = () => {
  const { user, loading } = useAuth();

  // Show loading state while verifying token
  if (loading) {
    return <div className="loading">Loading...</div>; // Replace with your loading UI
  }

  // Redirect to login if user isn't authenticated
  return user ? <Outlet /> : <Navigate to="/login" replace />;
};

Configure Your Routes

Wrap your app with the AuthProvider and use ProtectedRoute for restricted pages:

import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { AuthProvider } from './AuthContext';
import { ProtectedRoute } from './ProtectedRoute';
import Login from './pages/Login';
import Dashboard from './pages/Dashboard';
import Profile from './pages/Profile';
import Home from './pages/Home';

function App() {
  return (
    <AuthProvider>
      <Router>
        <Routes>
          {/* Public routes */}
          <Route path="/" element={<Home />} />
          <Route path="/login" element={<Login />} />

          {/* Protected routes (only accessible if authenticated) */}
          <Route element={<ProtectedRoute />}>
            <Route path="/dashboard" element={<Dashboard />} />
            <Route path="/profile" element={<Profile />} />
          </Route>

          {/* Catch-all route for 404 */}
          <Route path="*" element={<Navigate to="/" replace />} />
        </Routes>
      </Router>
    </AuthProvider>
  );
}

export default App;

Example Login Page

Implement a login form that uses the useAuth hook:

import { useState } from 'react';
import { useAuth } from '../AuthContext';
import { useNavigate } from 'react-router-dom';

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

  const handleSubmit = async (e) => {
    e.preventDefault();
    setError('');
    
    const result = await login(email, password);
    if (result.success) {
      navigate('/dashboard'); // Redirect to protected page on success
    } else {
      setError(result.message);
    }
  };

  return (
    <div className="login-container">
      <h2>Login</h2>
      {error && <p className="error-message">{error}</p>}
      <form onSubmit={handleSubmit}>
        <div className="form-group">
          <label>Email:</label>
          <input
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            required
          />
        </div>
        <div className="form-group">
          <label>Password:</label>
          <input
            type="password"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
            required
          />
        </div>
        <button type="submit" className="login-btn">Login</button>
      </form>
    </div>
  );
};

export default Login;

3. Key Best Practices
  • Never hardcode your JWT secret: Store it in a .env file and use dotenv in Express to access it.
  • Set short token expiration times: 1 hour is standard. Add a refresh token flow if you need longer sessions.
  • Handle token expiration: In your frontend, catch 403 errors from API calls, clear the token, and redirect to login.
  • Be mindful of XSS risks: Storing JWT in localStorage is common but vulnerable to XSS attacks. If security is critical, consider using HTTP-only cookies instead (requires backend adjustments).

内容的提问来源于stack exchange,提问作者vidu.sh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:39:22