React路由鉴权失效求助:未登录可直接访问Dashboard页面
问题描述
我正在开发一个网站,已完成注册页面、登录页面和Dashboard页面。我希望用户仅在通过登录页面成功登录后,才能访问Dashboard页面,但目前直接在地址栏输入'/dashboard'就能显示该页面。请帮忙指出代码中的问题。
后端路由文件代码
const express = require ("express") const Resident = require("../models/residentModels"); const bcrypt = require("bcrypt"); const auth = require("../middleware/auth"); const router = express.Router(); router.post("/register", async (req, res) => { try { const resident = new Resident(req.body); console.log(req.body); // password hash // middleware // const token = await resident.generateAuthToken(); // console.log("token: " + token); // res.cookie("jwt", token, { // httpOnly: true, // expires: new Date (Date.now() + 12342) // }) // console.log(cookie); const newResident = await resident.save(); console.log("req.cookies.jwt: " + req.cookies.jwt); console.log("hehe" + newResident); // console.log(newResident._id); // console.log(newResident._id.toString()); res.status(200).send("Record Added Successfully"); } catch (error) { console.log(error); res.status(200).send("Record Creation Unsuccessfull"); } }) router.post("/login", async (req, res) => { try { let blockNumber = req.body.blockNumber; let flatNumber = req.body.flatNumber; let password = req.body.password; // console.log(blockNumber) // console.log(flatNumber) // console.log(password) const response = await Resident.findOne({blockNumber: blockNumber, flatNumber:flatNumber}); console.log("response: " + response); const isMatch = await bcrypt.compare(password, response.password); console.log("isMatch: " + isMatch); // const token = await response.generateAuthToken(); // console.log(token); const token = await response.generateAuthToken(); console.log("token part: " + token); res.cookie("jwt", token, { httpOnly: true, expires: new Date(Date.now() + 300000) // secure: true }) // console.log(cookie) // res.header("Authorization", token); console.log(`this is the cookie awesome !! + ${req.cookies.jwt}`); // will return undefined as same page generating cookies will not access it if(isMatch){ res.status(200).send("Successful Login"); } else{ // res.status(200).send("Invalid Login Details"); } res.status(200).send({ user, token: jwt.token }); } catch (error) { // res.status(200).send("Invalid Login Details"); } }); router.get("/dashboard", auth, async(req, res) => { console.log("token: " + req.cookies.jwt); res.status(200).send(req.cookies.jwt); }) router.get("/logout", auth, async(req, res) => { try { console.log("logout successfully"); } catch (error) { res.status(500).send(error); } }) router.get("/festivals", auth, async(req, res) => { console.log(req.cookies.jwt); res.status(200).send(req.cookies.jwt); }) module.exports = router;
React前端App.js文件代码
import { BrowserRouter, Route, Routes } from 'react-router-dom'; import Home from './components/pages/Utility/Home'; import Gallery from './components/pages/Utility/Gallery'; import Teams from './components/pages/Utility/Teams'; import Login from './components/pages/Resident/Login'; import Register from './components/pages/Resident/Register'; import Dashboard from './components/pages/Resident/Dashboard'; import Festivals from './components/pages/Resident/Festivals'; import DurgaPuja from './components/pages/Resident/DurgaPuja'; import DurgaPujaBhog from './components/pages/Resident/DurgaPujaBhog'; import QRCode from './components/pages/Resident/QRCode'; import AdminLogin from './components/pages/Admin/AdminLogin'; import AdminPanel from './components/pages/Admin/AdminPanel'; import AdminPayment from './components/pages/Admin/AdminPayment'; import AdminBhogDistribution from './components/pages/Admin/AdminBhogDistribution'; import Navbar from './components/Navbar'; import Logout from './components/pages/Utility/Logout'; import AuthApi from './utils/AuthApi'; import { useState } from 'react'; function App() { const [auth, setAuth] = useState(false); return ( <> {/* <AuthApi.Provider value={{auth, setAuth}}> */} <BrowserRouter> <Navbar /> <Routes> <Route path='/' element={<Home />} /> <Route path='/home' element={<Home />} /> <Route path='/gallery' element={<Gallery />} /> <Route path='/team' element={<Teams />} /> <Route path='/login' element={<Login />} /> <Route path='/register' element={<Register />} /> <Route path='/dashboard' element={<Dashboard />} /> <Route path='/festivals' element={<Festivals />} /> <Route path='/durga-puja' element={<DurgaPuja />} /> <Route path='/durga-puja-bhog' element={<DurgaPujaBhog />} /> <Route path='/qr-code' element={<QRCode />} /> <Route path='/logout' element={<Logout />} /> {/* Admin Panel */} <Route path='/admin-login' element={<AdminLogin />} /> <Route path='/admin-panel' element={<AdminPanel />} /> <Route path='/admin-payment' element={<AdminPayment />} /> <Route path='/admin-bhog-distribution' element={<AdminBhogDistribution />} /> </Routes> </BrowserRouter> {/* </AuthApi.Provider> */} </> ); } export default App;
Login.jsx文件代码
import axios from 'axios'; import React from 'react' import { NavLink, useNavigate } from 'react-router-dom' const Login = () => { let navigate = useNavigate(); const loginButton = async (event) => { event.preventDefault(); // console.log(event.target[0].value); // block number let blockNumber = event.target[0].value; let flatNumber = event.target[1].value; let password = event.target[2].value; const response = await axios.post('http://localhost:8000/login', { blockNumber: blockNumber, flatNumber: flatNumber, password: password }, { withCredentials: true }) let myToken = axios.get("http://localhost:8000/dashboard", { withCredentials: true }).then((res)=> console.log(res.data)); console.log(myToken); // console.log("response :" + response.data); console.log(response); if (response.status === 200) { alert("Login Successful"); navigate("/dashboard"); } else { alert("Invalid Login Details"); navigate("/dashboard"); } } return ( <> <h1> Login </h1> <form onSubmit={loginButton}> <input type="number" placeholder='Block Number *' /> <input type="number" placeholder='Flat Number *' /> <input type="password" placeholder='Password *' /> <button type="submit">Login</button> <NavLink to='/register'>or Click to Register</NavLink> </form> </> ) } export default Login
模型Schema文件代码
const validator = require('validator'); const mongoose = require("mongoose"); const bcrypt = require("bcrypt"); const jwt = require("jsonwebtoken"); const residentSchema = mongoose.Schema({ blockNumber: { type: Number, required: true }, flatNumber: { type: Number, required: true }, name: { type: String, required: true }, password: { type: String, required: true }, email: { type: String, validate: { validator: validator.isEmail, message: "please enter a valid email address" } }, phone : { type: Number, required: true }, saptami: [], astami: [], nabami: [], dasami: [], tokens: [{ token:{ type: String, required: true } }] }); residentSchema.methods.generateAuthToken = async function () { try { console.log(this._id); const token = jwt.sign({_id: this._id.toString()}, "thisisnotmyresumeprojectbutapassion", { expiresIn: 300 }); this.tokens = this.tokens.concat({token: token}); await this.save(); return token; } catch (error) { res.send(error); console.log(error); } } residentSchema.pre("save", async function (next){ if(this.isModified("password")){ // const passwordHash = await bcrypt.hash(password, 10); console.log(`the current password is ${this.password}`); this.password = await bcrypt.hash(this.password, 10); } next(); }) const Resident = new mongoose.model("Resident", residentSchema); module.exports = Resident;
核心问题与修复方案
1. 前端路由无登录校验
当前/dashboard等敏感路由直接开放,没有做登录状态拦截。需要添加受保护路由组件,校验全局登录状态:
// App.js中新增受保护路由组件 const ProtectedRoute = ({ children }) => { return auth ? children : <Navigate to="/login" />; }; // 替换原有敏感路由配置,例如: <Route path='/dashboard' element={ <ProtectedRoute> <Dashboard /> </ProtectedRoute> } />
2. 全局状态管理未启用
取消AuthApi.Provider的注释,让全局登录状态可以在路由和组件间共享:
// App.js中取消注释 <AuthApi.Provider value={{auth, setAuth}}> {/* 原有BrowserRouter代码 */} </AuthApi.Provider>
3. 后端登录逻辑漏洞
- 登录接口无论密码是否匹配,最终都会返回200状态码,且错误处理被注释,前端无法判断登录结果。
- 最后一行
res.status(200).send({ user, token: jwt.token });中user和jwt未定义,会导致服务器报错。
修复后的登录接口:
router.post("/login", async (req, res) => { try { const { blockNumber, flatNumber, password } = req.body; const resident = await Resident.findOne({ blockNumber, flatNumber }); if (!resident) { return res.status(401).send("Invalid Login Details"); } const isMatch = await bcrypt.compare(password, resident.password); if (!isMatch) { return res.status(401).send("Invalid Login Details"); } const token = await resident.generateAuthToken(); res.cookie("jwt", token, { httpOnly: true, expires: new Date(Date.now() + 300000) }); res.status(200).send({ message: "Successful Login" }); } catch (error) { console.log(error); res.status(500).send("Server Error"); } });
4. 前端Login组件逻辑错误
无论登录成功与否,都会跳转到/dashboard,完全失去校验意义。修复后仅在登录成功时跳转并更新全局状态:
// Login.jsx中引入上下文并修改逻辑 import { useContext } from 'react'; import AuthApi from '../utils/AuthApi'; const Login = () => { const { setAuth } = useContext(AuthApi); const loginButton = async (event) => { event.preventDefault(); // ...获取表单值逻辑 try { const response = await axios.post('http://localhost:8000/login', { blockNumber, flatNumber, password }, { withCredentials: true }); if (response.status === 200) { alert("Login Successful"); setAuth(true); navigate("/dashboard"); } } catch (error) { alert("Invalid Login Details"); // 登录失败不跳转,留在登录页 } } }
5. 确保auth中间件正确验证token
检查../middleware/auth.js是否正确解析cookie中的JWT并验证用户身份,示例实现:
const jwt = require('jsonwebtoken'); const Resident = require('../models/residentModels'); const auth = async (req, res, next) => { try { const token = req.cookies.jwt; if (!token) return res.status(401).send("Please login first"); const decoded = jwt.verify(token, "thisisnotmyresumeprojectbutapassion"); const resident = await Resident.findOne({ _id: decoded._id, "tokens.token": token }); if (!resident) throw new Error(); req.resident = resident; next(); } catch (error) { res.status(401).send("Authentication failed"); } } module.exports = auth;
内容的提问来源于stack exchange,提问作者Shanai
相关产品推荐
相关产品推荐

