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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 07:20:35