基于Node、React、MongoDB实现认证用户类型识别方案咨询
当然可以用JWT来实现这个需求!我来给你拆解下基于Node.js + React + MongoDB的完整方案,分后端和前端两部分一步步讲:
后端实现(Node.js + MongoDB)
1. 定义用户模型
首先在MongoDB里创建用户集合,需要包含role字段来区分普通用户和管理员,用Mongoose的话可以这么写:
const mongoose = require('mongoose'); const userSchema = new mongoose.Schema({ email: { type: String, required: true, unique: true }, password: { type: String, required: true }, // 限定角色只能是user或admin,默认普通用户 role: { type: String, enum: ['user', 'admin'], default: 'user' } }); module.exports = mongoose.model('User', userSchema);
2. JWT生成与登录接口
先安装依赖:npm install jsonwebtoken bcryptjs,bcrypt用来加密存储密码,避免明文泄露。
然后写登录接口,验证用户身份后,把role字段放进JWT的payload里:
const jwt = require('jsonwebtoken'); const bcrypt = require('bcryptjs'); const User = require('../models/User'); // 登录接口 exports.login = async (req, res) => { try { const { email, password } = req.body; // 查找用户 const user = await User.findOne({ email }); if (!user) return res.status(400).json({ message: '用户不存在' }); // 验证密码 const isPasswordMatch = await bcrypt.compare(password, user.password); if (!isPasswordMatch) return res.status(400).json({ message: '密码错误' }); // 生成JWT,包含用户ID和角色 const token = jwt.sign( { userId: user._id, role: user.role }, process.env.JWT_SECRET, // 密钥存环境变量,绝对不能硬编码 { expiresIn: '7d' } // 按需设置过期时间 ); // 返回token和基本用户信息 res.json({ token, user: { id: user._id, email: user.email, role: user.role } }); } catch (err) { res.status(500).json({ message: '服务器内部错误' }); } };
3. JWT验证与权限中间件
写一个中间件用来验证请求中的JWT,并解析出用户信息,后续接口用这个中间件保护:
exports.protect = async (req, res, next) => { let token; // 从请求头的Authorization字段获取token(格式:Bearer <token>) if (req.headers.authorization?.startsWith('Bearer')) { token = req.headers.authorization.split(' ')[1]; } if (!token) return res.status(401).json({ message: '未授权,请先登录' }); try { // 验证token并解析payload const decoded = jwt.verify(token, process.env.JWT_SECRET); // 把用户信息挂载到req对象上,方便后续接口使用 req.user = await User.findById(decoded.userId).select('-password'); next(); } catch (err) { res.status(401).json({ message: 'Token无效或已过期' }); } };
如果需要单独限制管理员才能访问的接口,再写一个管理员权限中间件:
exports.adminOnly = (req, res, next) => { if (req.user?.role === 'admin') { next(); } else { res.status(403).json({ message: '无管理员权限' }); } };
然后就能用这些中间件保护接口了:
// 普通用户和管理员都能访问的接口 router.get('/profile', authController.protect, userController.getProfile); // 仅管理员能访问的接口 router.get('/all-users', authController.protect, authController.adminOnly, userController.getAllUsers);
前端实现(React)
1. 登录状态与角色管理
用Context API来全局管理用户登录状态和角色信息(用Redux也可以,看你技术选型):
import { createContext, useContext, useState, useEffect } from 'react'; const AuthContext = createContext(); export const AuthProvider = ({ children }) => { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); // 页面加载时从localStorage恢复登录状态 useEffect(() => { const storedUser = localStorage.getItem('user'); if (storedUser) setUser(JSON.parse(storedUser)); setLoading(false); }, []); // 登录函数 const login = async (email, password) => { try { const res = await fetch('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }) }); const data = await res.json(); if (!res.ok) throw new Error(data.message); setUser(data.user); localStorage.setItem('user', JSON.stringify(data.user)); localStorage.setItem('token', data.token); } catch (err) { alert(err.message); } }; // 登出函数 const logout = () => { setUser(null); localStorage.removeItem('user'); localStorage.removeItem('token'); }; return ( <AuthContext.Provider value={{ user, login, logout, loading }}> {!loading && children} </AuthContext.Provider> ); }; export const useAuth = () => useContext(AuthContext);
记得在App.js里用AuthProvider包裹所有组件,让全局都能访问到登录状态。
2. 路由权限控制
用React Router实现路由保护,区分普通用户和管理员路由:
私有路由(需登录)
import { Navigate } from 'react-router-dom'; import { useAuth } from '../contexts/AuthContext'; const PrivateRoute = ({ children }) => { const { user } = useAuth(); if (!user) return <Navigate to="/login" replace />; return children; }; export default PrivateRoute;
管理员路由(需管理员权限)
import { Navigate } from 'react-router-dom'; import { useAuth } from '../contexts/AuthContext'; const AdminRoute = ({ children }) => { const { user } = useAuth(); if (!user || user.role !== 'admin') return <Navigate to="/" replace />; return children; }; export default AdminRoute;
然后配置路由:
import { Routes, Route } from 'react-router-dom'; import Login from './pages/Login'; import Home from './pages/Home'; import AdminDashboard from './pages/AdminDashboard'; import PrivateRoute from './components/PrivateRoute'; import AdminRoute from './components/AdminRoute'; const AppRoutes = () => { return ( <Routes> <Route path="/login" element={<Login />} /> {/* 普通用户和管理员都能访问的私有路由 */} <Route path="/" element={ <PrivateRoute> <Home /> </PrivateRoute> } /> {/* 仅管理员能访问的路由 */} <Route path="/admin" element={ <AdminRoute> <AdminDashboard /> </AdminRoute> } /> </Routes> ); }; export default AppRoutes;
3. 页面内容动态渲染
在组件里通过useAuth获取用户角色,动态渲染对应内容:
import { useAuth } from '../contexts/AuthContext'; const Home = () => { const { user } = useAuth(); return ( <div className="home"> <h1>欢迎回来,{user.email}</h1> {/* 管理员专属内容 */} {user.role === 'admin' && ( <div className="admin-section"> <p>你是管理员,拥有全部权限</p> <button>前往用户管理面板</button> </div> )} {/* 普通用户专属内容 */} {user.role === 'user' && ( <p>你可以查看和编辑个人信息</p> )} </div> ); }; export default Home;
4. 请求自动携带JWT
封装一个带认证的fetch工具函数,每次请求自动把token放到请求头:
const fetchWithAuth = async (url, options = {}) => { const token = localStorage.getItem('token'); const headers = { 'Content-Type': 'application/json', ...options.headers }; if (token) headers.Authorization = `Bearer ${token}`; return fetch(url, { ...options, headers }); }; // 使用示例 const fetchProfile = async () => { const res = await fetchWithAuth('/api/users/profile'); const data = await res.json(); // 处理数据 };
注意事项
- JWT密钥一定要存在环境变量里,绝对不能硬编码到代码中
- 用户密码必须用bcrypt加密存储,禁止明文存储
- 前端用localStorage存token有XSS风险,若要更安全可以考虑用HttpOnly Cookie(需要后端配置跨域Cookie)
- 后端要配置CORS,允许前端跨域请求
内容的提问来源于stack exchange,提问作者jsdev17
相关产品推荐
相关产品推荐

