MERN栈JWT+HTTP Only Cookie认证后React组件延迟渲染求助
MERN栈用户认证系统重定向后用户名延迟显示问题排查
我基于MERN栈开发了一套用户认证系统,采用JWT与HTTP Only Cookie实现身份验证。登录时后端验证密码后会在浏览器中设置Cookie,登录成功后页面会重定向至userpage.jsx。该页面需要通过Cookie获取用户信息并渲染用户名,但用户名需要等待约20秒才会显示;若在重定向后刷新页面,用户名则能立即显示。不过在Firefox浏览器中未出现此问题,登录重定向后可正常显示用户名。请帮忙排查问题原因。
Backend 代码
const UserModel = require('../models/registration') const bcrypt = require('bcrypt') const jwt = require('jsonwebtoken') const dotenv = require('dotenv') dotenv.config() const signup = async(req,res)=>{ try{ const hash = await bcrypt.hash(req.body.password,10) const uer = new UserModel({ name:req.body.name, email:req.body.email, password: hash }) await uer.save(); console.log("User Created") res.send("User Created") }catch(e){ console.log(e) res.status(500).json("User not created") } } const login = async(req,res)=>{ try{ const exist = await UserModel.findOne({name:req.body.user}) // console.log(exist._id) if(exist){ const chk = await bcrypt.compare(req.body.pass,exist.password); if(chk){ const token = jwt.sign({ id:exist._id, name:exist.name },process.env.JWT,{ expiresIn:'1h' }) res.cookie(String(exist._id),token,{ path:'/', expires:new Date(Date.now()+1000*15), httpOnly: true, sameSite: 'lax' }) console.log("Logged In") res.json({status:'success',token,id:exist}) } else{ res.status(401).json("wrong Pass") } } else{ req.status(401).send("User not Found!") } }catch(e){ console.log(e) res.status(500).json("User Not found") } } const varifyToken = async(req,res,next)=>{ const cookies = req.headers.cookie console.log(cookies); if(cookies){ const authToken = cookies.split("=")[1] console.log("Verifying.....") try{ const validate = await jwt.verify(String(authToken),process.env.JWT) console.log(validate) if(validate){ req.Id = validate.id // res.send({id:validate}) next() } } catch(er){ console.log("Verify Token Error"); } } } const getUser = async(req,res)=>{ const Id = req.Id; // console.log(Id) try{ user = await UserModel.findById(Id,"-password") if(!user){ res.status(401).json("user not found") } // res.status(200).send({user}) res.status(200).send(user) } catch(e){ console.log(e) } } module.exports = { signup, login, varifyToken, getUser }
路由文件代码
const express = require('express'); const router = express.Router(); const {signup,login,varifyToken, getUser} = require('../controller/userController') router.post('/signup',signup) router.post('/login',login) router.get('/user', varifyToken,getUser) module.exports = router;
入口文件代码
const express = require('express') const mongoose = require('mongoose') const router = require('./routes/userRoutes') const cookieParser =require('cookie-parser') const cors = require('cors') mongoose.connect('mongodb://localhost:27017/Authentication').then(()=>{ console.log("Mongodb Connected") }).catch(err=>console.log("Monodb problem")) const app = express() app.use(cors({credentials:true, origin:"http://localhost:3000"})) app.use(express.json()) app.use(cookieParser()) app.use('/',router) app.listen(8800,()=>{ console.log('Server is Running...') })
登录页组件代码
import axios from "axios"; import { useState } from "react"; import { useNavigate } from 'react-router-dom'; import './reg.css'; export default function Login(){ const navigate = useNavigate() const [data,setData] = useState({ user:'', pass:'', }) const dataSend = async()=>{ const {pass,user} = data; try{ const res = await axios.post("http://localhost:8800/login",{ pass,user }) // const Res = res.data; return res.data } catch(err){ console.log("Login : Error In DataSend") } } const handleChange = (e)=>{ setData((prev)=>({ ...prev, [e.target.name]: e.target.value })) } const handleSubmit = async(e)=>{ e.preventDefault(); try{ const Data = await dataSend() console.log(Data) } catch(er){ console.log("Error On Handle SUbmit In Login") } navigate('/') } return( <div className="main"> <h1>Login</h1> <form onSubmit={handleSubmit}> <input type="text" value={data.user} name='user' onChange={handleChange} /> <br /> <input type="password" name="pass" value={data.pass} onChange={handleChange} /> <br /> <input type="submit" value="Submit"/> </form> </div> ) }
用户页组件代码
import axios from "axios"; import { useEffect, useState } from "react"; axios.defaults.withCredentials= true; export default function UserPage(){ const [user,setUser] = useState(); //const [userData, setUserData] = useState(); const sendRequest = async()=>{ try{ console.log("I am Calling Get Request...") const res = await axios.get('http://localhost:8800/user',{ withCredentials:true }) return res.data } catch(er){ console.log("User: Error In sendRequest") } } useEffect(()=>{ sendRequest().then((p)=> setUser(p.name)) // if(user){ // setUserData(user) // } },[]) // I used dependency as user but never works return( <div> {user? <h3>Hello {userData}</h3> : <h3>Guest User</h3>} </div> ) }
问题原因排查与解决方案
1. 登录后重定向时机错误
你的登录提交逻辑中,不管登录请求是否成功、Cookie是否完成写入,都会执行navigate('/')。部分浏览器(如Chrome)在Cookie写入前跳转,新页面的请求不会携带刚设置的Cookie,导致/user接口验证失败,请求挂起直到Cookie延迟生效或超时重试,出现20秒延迟。
修复:仅在登录请求成功后再重定向:
const handleSubmit = async(e)=>{ e.preventDefault(); try{ const Data = await dataSend() if(Data?.status === 'success'){ // 确认登录响应正常 navigate('/') } } catch(er){ console.log("Error On Handle SUbmit In Login") } }
2. Cookie解析逻辑存在漏洞
你用用户ID作为Cookie的动态key,但varifyToken中直接用cookies.split("=")[1]取Token,当浏览器存在多个Cookie时,会拿到错误的值,导致Token验证失败,请求挂起。
修复:给Cookie设置固定名称,统一解析逻辑:
- 后端
login接口修改Cookie设置:
res.cookie('authToken', token, { path:'/', expires:new Date(Date.now()+3600*1000), // 和Token过期时间保持一致(1小时) httpOnly: true, sameSite: 'lax' })
- 修改
varifyToken解析逻辑:
const varifyToken = (req,res,next)=>{ // jwt.verify是同步方法,不需要async const cookies = req.headers.cookie console.log(cookies); if(cookies){ const tokenMatch = cookies.match(/authToken=([^;]+)/); if(tokenMatch){ const authToken = tokenMatch[1]; console.log("Verifying.....") try{ const validate = jwt.verify(String(authToken),process.env.JWT) console.log(validate) if(validate){ req.Id = validate.id next() } } catch(er){ console.log("Verify Token Error"); res.status(401).json("Invalid token") // 主动返回错误,避免请求挂起 } } else { res.status(401).json("No auth token found") } } else { res.status(401).json("No cookies present") } }
3. 用户页渲染变量错误
用户页中定义了user状态,但渲染时用的是已注释的userData,导致渲染异常,即使请求成功也无法正确显示用户名。
修复:使用正确的状态变量渲染:
return( <div> {user? <h3>Hello {user}</h3> : <h3>Guest User</h3>} </div> )
内容的提问来源于stack exchange,提问作者Ahsan Amin
相关产品推荐
相关产品推荐

