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

如何为React前端在NodeJS Express中实现API身份验证及Passport方案

如何在React+Express应用中实现API身份验证并支持Passport选项

我来一步步帮你搞定这个需求——在React+Express栈里实现API身份验证,同时支持Passport的各种验证方案。下面是我实战过的流程,亲测好用:


一、前期依赖安装

首先得把需要的包装上,终端里分别在后端和前端执行:

后端(Express)

npm install express passport passport-local passport-jwt jsonwebtoken bcryptjs mongoose cors
  • passport-local:处理用户名密码登录
  • passport-jwt:用JWT保护API路由
  • bcryptjs:加密存储用户密码
  • mongoose:如果用MongoDB做数据库(用其他数据库的话替换成对应ORM/驱动)

前端(React)

npm install axios react-router-dom
  • axios:发送API请求
  • react-router-dom:处理前端路由保护

二、Express后端核心配置

1. 用户数据库模型

先定义用户模型(以MongoDB为例),确保密码是加密存储的:

// models/User.js
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');

const userSchema = new mongoose.Schema({
  email: { type: String, required: true, unique: true },
  password: { type: String, required: true }
});

// 保存前加密密码
userSchema.pre('save', async function(next) {
  if (!this.isModified('password')) return next();
  this.password = await bcrypt.hash(this.password, 10);
  next();
});

// 验证密码方法
userSchema.methods.matchPassword = async function(enteredPassword) {
  return await bcrypt.compare(enteredPassword, this.password);
};

module.exports = mongoose.model('User', userSchema);

2. Passport策略配置

创建config/passport.js文件,配置本地策略和JWT策略:

const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const User = require('../models/User');

// 本地策略:验证用户名密码
passport.use(new LocalStrategy({ usernameField: 'email' }, async (email, password, done) => {
  try {
    const user = await User.findOne({ email });
    if (!user) return done(null, false, { message: 'User not found' });
    
    const isMatch = await user.matchPassword(password);
    if (!isMatch) return done(null, false, { message: 'Invalid credentials' });
    
    return done(null, user);
  } catch (err) {
    return done(err);
  }
}));

// JWT策略:验证请求中的token
const jwtOptions = {
  jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
  secretOrKey: process.env.JWT_SECRET // 要在.env文件里设置这个密钥
};

passport.use(new JwtStrategy(jwtOptions, async (jwtPayload, done) => {
  try {
    const user = await User.findById(jwtPayload.userId);
    if (user) return done(null, user);
    return done(null, false);
  } catch (err) {
    return done(err);
  }
}));

module.exports = passport;

3. 初始化Express和Auth路由

在server.js里初始化Passport,并编写注册、登录和受保护路由:

require('dotenv').config();
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const passport = require('./config/passport');
const jwt = require('jsonwebtoken');
const User = require('./models/User');

const app = express();

// 中间件
app.use(cors());
app.use(express.json());
app.use(passport.initialize());

// 连接数据库
mongoose.connect(process.env.MONGO_URI)
  .then(() => console.log('DB connected'))
  .catch(err => console.log(err));

// 注册路由
app.post('/api/auth/register', async (req, res) => {
  try {
    const { email, password } = req.body;
    const existingUser = await User.findOne({ email });
    if (existingUser) return res.status(400).json({ message: 'User already exists' });
    
    const user = await User.create({ email, password });
    const token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, { expiresIn: '30d' });
    
    res.status(201).json({ token, user: { id: user._id, email: user.email } });
  } catch (err) {
    res.status(500).json({ message: 'Server error' });
  }
});

// 登录路由:用passport本地策略验证
app.post('/api/auth/login', passport.authenticate('local', { session: false }), (req, res) => {
  const token = jwt.sign({ userId: req.user._id }, process.env.JWT_SECRET, { expiresIn: '30d' });
  res.json({ token, user: { id: req.user._id, email: req.user.email } });
});

// 受保护路由示例:用passport JWT策略验证
app.get('/api/protected', passport.authenticate('jwt', { session: false }), (req, res) => {
  res.json({ message: 'This is protected data', user: req.user });
});

const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));

三、React前端集成

1. 登录/注册组件示例

写一个简单的登录组件,处理表单提交并保存token:

// src/components/Login.js
import { useState } from 'react';
import axios from 'axios';
import { useNavigate } from 'react-router-dom';

const Login = () => {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const navigate = useNavigate();

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      const res = await axios.post('http://localhost:5000/api/auth/login', { email, password });
      localStorage.setItem('token', res.data.token);
      navigate('/dashboard'); // 登录成功跳转到受保护页面
    } catch (err) {
      alert(err.response.data.message);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
        required
      />
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        placeholder="Password"
        required
      />
      <button type="submit">Login</button>
    </form>
  );
};

export default Login;

2. 保护前端路由

创建一个高阶组件(HOC)来保护需要登录才能访问的路由:

// src/components/ProtectedRoute.js
import { Navigate, Outlet } from 'react-router-dom';

const ProtectedRoute = () => {
  const token = localStorage.getItem('token');
  return token ? <Outlet /> : <Navigate to="/login" />;
};

export default ProtectedRoute;

然后在路由配置里使用:

// src/App.js
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Login from './components/Login';
import Dashboard from './components/Dashboard';
import ProtectedRoute from './components/ProtectedRoute';

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/login" element={<Login />} />
        <Route element={<ProtectedRoute />}>
          <Route path="/dashboard" element={<Dashboard />} />
        </Route>
      </Routes>
    </Router>
  );
}

export default App;

3. 发送带身份验证的请求

封装axios实例,自动在请求头里添加token:

// src/utils/axiosInstance.js
import axios from 'axios';

const axiosInstance = axios.create({
  baseURL: 'http://localhost:5000/api'
});

// 请求拦截器:添加token
axiosInstance.interceptors.request.use((config) => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

export default axiosInstance;

然后在Dashboard组件里使用:

// src/components/Dashboard.js
import { useState, useEffect } from 'react';
import axiosInstance from '../utils/axiosInstance';

const Dashboard = () => {
  const [data, setData] = useState(null);

  useEffect(() => {
    const fetchProtectedData = async () => {
      try {
        const res = await axiosInstance.get('/protected');
        setData(res.data);
      } catch (err) {
        alert('Unauthorized');
      }
    };
    fetchProtectedData();
  }, []);

  return (
    <div>
      <h1>Dashboard</h1>
      {data && <p>{data.message}</p>}
    </div>
  );
};

export default Dashboard;

四、扩展Passport其他策略(可选)

如果要支持Google OAuth这类第三方登录,步骤类似:

  1. 安装对应Passport策略包(比如passport-google-oauth20)
  2. 在Passport配置里添加Google策略,设置客户端ID和密钥
  3. 编写OAuth路由(比如/api/auth/google和/api/auth/google/callback)
  4. 前端添加Google登录按钮,跳转到后端OAuth路由,回调后拿到token并存入localStorage

内容的提问来源于stack exchange,提问作者Ichrak Mansour

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:07:46