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

React页面刷新后userToken验证失效跳转登录页问题解决

React 页面刷新后私有路由跳转到登录页问题

开发React应用时,已配置私有路由和基于角色的权限路由,登录后系统运行正常,但页面刷新(重新渲染)时会被跳转到登录页,尽管token仍保存在localStorage中。checkLogin方法可能未正确调用或生效,首次加载时useAuth中的userToken存在,但渲染后变为null并触发跳转。

私有路由组件代码

const PrivateRoutes = () => {
    const {userToken} = useAuth();
 
    return userToken ? <Outlet /> : <Navigate to="/login" replace />;
}

export default PrivateRoutes;

function AppRoutes() {
    return (
        <Routes>
            <Route element={<Login />} path="/login" />
            <Route element={<Register />} path="/register" />
            <Route element={<NoMatch />} path="*" />

            <Route element={<PrivateRoutes />}>
                <Route element={<MainLayout />} path='/'>
                    <Route element={<MyProfile />} path='/myprofile' />
                    <Route element={<ChangePassword />} path='/changepassword' />
                    <Route element={<ProtectedRoute roles={[ROLES.CLIENT]} component={ClientUserList} />} path="/client/user/list" />
                    <Route element={<ProtectedRoute roles={[ROLES.CLIENT]} component={ClientUserCreate} />} path="/client/user/create" />
                    <Route element={<ProtectedRoute roles={[ROLES.CLIENT]} component={ClientUserEdit} />} path="/client/user/edit/:id" />
                    <Route element={<ProtectedRoute roles={[ROLES.CLIENT]} component={ClientUserShow} />} path="/client/user/show/:id" />
                    <Route element={<ProtectedRoute roles={[ROLES.CLIENT]} component={ManageInfo} />} path="/manage" />
                    <Route element={<ProtectedRoute roles={[ROLES.CLIENT]} component={ManageEdit} />} path="/manage/edit" />
                    <Route element={<ProtectedRoute roles={[ROLES.CLIENT]} component={CategoryList} />} path="/category/list" />
                </Route>
            </Route>
        </Routes>
    )
}

AuthContext 代码

const AuthContext = createContext({});

const AuthProvider = ({ children }) => {
    const [userToken, setUserToken] = useState(null);
    const [userInfo, setUserInfo] = useState(null);
    const navigate = useNavigate();

    const checkLogin = async () => {
        console.log("checking login info");
        const tokenInfo = JSON.parse(localStorage.getItem("userToken"));
        if (tokenInfo != null) {
            setUserToken(tokenInfo);
            var userData = await accountService.getInfo();
            if (userData.result > 0) {
                localStorage.setItem("userInfo", JSON.stringify(userData));
                setUserInfo(userData);
            } else {
                localStorage.removeItem("userToken");
                localStorage.removeItem("userInfo");
                setUserToken(null);
                setUserInfo(null);
                navigate("/login");
            }
        } else {
            setUserToken(null);
        }
    };

    const doLogin = (tokenData) => {
        console.log("token",tokenData);
        if (tokenData.result > 0) {
            localStorage.setItem("userToken", JSON.stringify(tokenData));
            setUserToken(tokenData);
            navigate("/");
            getInfo(); // 注意:此处getInfo未定义,存在逻辑漏洞
        }
    };

    return (
        <AuthContext.Provider value={{ userToken, userInfo, checkLogin, doLogin }}>
            {children}
        </AuthContext.Provider>
    );
};

export const useAuth = () => useContext(AuthContext);

ProtectedRoute 组件代码

function ProtectedRoute({ component: Component, roles, ...rest }) {
    const { userToken } = useAuth();
    const token= userToken.data.token;
    const userRole = getRole(token);

    if (!userToken) {
        return <Navigate to="/login" replace />;
    }

    if (roles && roles.indexOf(userRole) === -1) {
        return <Navigate to="/myprofile" replace />;
    }

    return <Component {...rest} />;
}

问题原因及解决方案

1. 未初始化调用checkLogin

页面刷新时,AuthProvider的userToken初始值为null,但没有自动触发checkLogin读取localStorage中的token。需在AuthProvider中用useEffect在组件挂载时执行检查:

const AuthProvider = ({ children }) => {
    // ... 原有状态与方法

    useEffect(() => {
        checkLogin();
    }, []);

    // ... 返回Provider
};

2. 异步检查时的过早跳转

checkLogin是异步方法,页面初始渲染时userToken仍为null,私有路由会直接跳转登录页。需添加加载状态,等待检查完成再判断:

// 修改AuthProvider,添加加载状态
const AuthProvider = ({ children }) => {
    const [userToken, setUserToken] = useState(null);
    const [userInfo, setUserInfo] = useState(null);
    const [isLoading, setIsLoading] = useState(true);
    const navigate = useNavigate();

    const checkLogin = async () => {
        setIsLoading(true);
        const tokenInfo = JSON.parse(localStorage.getItem("userToken"));
        if (tokenInfo != null) {
            setUserToken(tokenInfo);
            try {
                const userData = await accountService.getInfo();
                if (userData.result > 0) {
                    localStorage.setItem("userInfo", JSON.stringify(userData));
                    setUserInfo(userData);
                } else {
                    localStorage.removeItem("userToken");
                    localStorage.removeItem("userInfo");
                    setUserToken(null);
                    setUserInfo(null);
                    navigate("/login");
                }
            } catch (err) {
                console.error("获取用户信息失败:", err);
                localStorage.removeItem("userToken");
                setUserToken(null);
                navigate("/login");
            }
        } else {
            setUserToken(null);
        }
        setIsLoading(false);
    };

    useEffect(() => {
        checkLogin();
    }, []);

    return (
        <AuthContext.Provider value={{ userToken, userInfo, isLoading, checkLogin, doLogin }}>
            {children}
        </AuthContext.Provider>
    );
};

修改PrivateRoutes:

const PrivateRoutes = () => {
    const { userToken, isLoading } = useAuth();

    if (isLoading) {
        return <div>加载中...</div>; // 替换为实际加载组件
    }

    return userToken ? <Outlet /> : <Navigate to="/login" replace />;
}

修改ProtectedRoute:

function ProtectedRoute({ component: Component, roles, ...rest }) {
    const { userToken, isLoading } = useAuth();

    if (isLoading) {
        return <div>加载中...</div>;
    }

    if (!userToken) {
        return <Navigate to="/login" replace />;
    }

    // 先判断userToken存在,避免访问data时报错
    const token = userToken.data.token;
    const userRole = getRole(token);

    if (roles && roles.indexOf(userRole) === -1) {
        return <Navigate to="/myprofile" replace />;
    }

    return <Component {...rest} />;
}

3. 修复doLogin中的未定义方法

doLogin里的getInfo()未定义,替换为checkLogin或直接实现用户信息获取逻辑:

const doLogin = (tokenData) => {
    console.log("token",tokenData);
    if (tokenData.result > 0) {
        localStorage.setItem("userToken", JSON.stringify(tokenData));
        setUserToken(tokenData);
        navigate("/");
        // 替换为已定义的checkLogin
        checkLogin();
    }
};

内容的提问来源于stack exchange,提问作者Goktug

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 08:20:11