如何为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这类第三方登录,步骤类似:
- 安装对应Passport策略包(比如
passport-google-oauth20) - 在Passport配置里添加Google策略,设置客户端ID和密钥
- 编写OAuth路由(比如
/api/auth/google和/api/auth/google/callback) - 前端添加Google登录按钮,跳转到后端OAuth路由,回调后拿到token并存入localStorage
内容的提问来源于stack exchange,提问作者Ichrak Mansour
相关产品推荐
相关产品推荐

