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

React集成ASP.NET API:登录权限控制及页面跳转问题求助

React路由权限控制问题:未登录用户仍能访问私有页面

我正在开发一款集成ASP.NET API的React应用,需求是未登录用户仅能访问登录页面,无法进入其他页面。我编写了如下代码,但未达到预期效果:

import React, { Fragment, useContext, useEffect, useState } from 'react';
import axios from 'axios';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import SignIn from '../pages/SignIn';
import Home from '../pages/Home'
import CadastroEmpresa from '../pages/CadastroEmpresa';
import CadastroIntegrante from '../pages/CadastroIntegrante';
import Empresas from '../pages/Empresas';
import useAuth from '../hooks/useAuth';
import { LoginContext } from '../context/auth';


const Private = ({ Item }) => {
  const signed = useAuth();

  return signed > 0 ? <Item /> : <SignIn />;
}

export { Private }

const RoutesApp = () => {
  return (
    <BrowserRouter>
      <Fragment>
        <Routes>
          <Route path='/' element={<SignIn />} />
          <Route path="*" element={<SignIn />} />
          <Route exact path='/home' element={<Private Item={Home} />} />
          <Route exact path='/empresas' element={<Private Item={Empresas} />} />
          <Route exact path='/cadastro-empresa' element={<Private Item={CadastroEmpresa} />} />
          <Route exact path='/cadastro-funcionario' element={<Private Item={CadastroIntegrante} />} />
        </Routes>
      </Fragment>
    </BrowserRouter>
  )
}

export default RoutesApp;

useAuth()是我基于以下Context实现的自定义Hook:

import React, { createContext, useState } from 'react';
import axios from 'axios';

const LoginContext = createContext();

const LoginProvider = ({ children }) => {
  const [loginData, setLoginData] = useState({ status: '', token: '' });

  const validateLogin = (loginUser) => {
    if (!loginUser.email || !loginUser.senha) {
      return false;
    }
    return true;
  };

  const passwordSignIn = async (email, senha) => {
    try {
      const response = await axios.post('https://localhost:7223/v1/entrar', {
        email,
        senha
      });
      console.log(response.data)
      return response.data;
    } catch (error) {
      console.error(error);
      return {};
    }
  };

  const generateToken = async (email, senha) => {
    try {
      const response = await axios.post('https://localhost:7223/v1/entrar', {
        email,
        senha
      });
      return response.data.token;
    } catch (error) {
      console.error(error);
      return '';
    }
  };

  const handleLogin = async (loginUser) => {
    if (!validateLogin(loginUser)) {
      return setLoginData({ status: 'error', token: '' });
    }

    const result = await passwordSignIn(loginUser.email, loginUser.senha);
    if (result.succeeded) {
      const token = await generateToken(loginUser.email);
      return setLoginData({ status: 'success', token: '' });
    }

    if (result.isLockedOut) {
      return setLoginData({ status: 'locked', token: '' });
    }

    console.log(loginUser.email)
    console.log(loginUser.senha)

    return setLoginData({ status: 'error', token: '' });
  };

  return (
    <LoginContext.Provider value={{ loginData, handleLogin }}>
      {children}
    </LoginContext.Provider>
  );
};

export { LoginContext, LoginProvider };

问题分析

  1. useAuth Hook实现缺失:仅提及基于LoginContext,但未提供具体实现,导致Private组件中signed > 0的判断无有效依据,无法正确识别登录状态。
  2. 登录逻辑错误:handleLogin中登录成功后错误将token设为空;generateToken重复调用登录接口,完全可复用passwordSignIn返回的token,无需重复请求。
  3. 私有路由判断逻辑不合理:用signed > 0判断登录状态不符合业务逻辑,应基于loginData的token是否存在或status是否为success来判断。
  4. 路由顺序错误:通配符路由(path="*")放在私有路由前会拦截所有后续路由,导致私有路由永远无法匹配。
  5. 未处理路由跳转:未登录访问私有路由时直接渲染SignIn但不修改URL,刷新页面后路径仍为私有路由,体验不佳。

解决方案

1. 完善useAuth自定义Hook

创建正确读取LoginContext的useAuth Hook:

// hooks/useAuth.js
import { useContext } from 'react';
import { LoginContext } from '../context/auth';

const useAuth = () => {
  const { loginData } = useContext(LoginContext);
  // 基于token和status判断登录状态
  const isAuthenticated = loginData.token && loginData.status === 'success';
  return isAuthenticated;
};

export default useAuth;

2. 修正LoginProvider中的登录逻辑

修复token赋值错误,移除重复请求:

// context/auth.js
import React, { createContext, useState } from 'react';
import axios from 'axios';

const LoginContext = createContext();

const LoginProvider = ({ children }) => {
  const [loginData, setLoginData] = useState({ status: '', token: '' });

  const validateLogin = (loginUser) => {
    if (!loginUser.email || !loginUser.senha) {
      return false;
    }
    return true;
  };

  const passwordSignIn = async (email, senha) => {
    try {
      const response = await axios.post('https://localhost:7223/v1/entrar', {
        email,
        senha
      });
      console.log(response.data)
      return response.data;
    } catch (error) {
      console.error(error);
      return {};
    }
  };

  const handleLogin = async (loginUser) => {
    if (!validateLogin(loginUser)) {
      return setLoginData({ status: 'error', token: '' });
    }

    const result = await passwordSignIn(loginUser.email, loginUser.senha);
    if (result.succeeded) {
      // 直接使用接口返回的token
      return setLoginData({ status: 'success', token: result.token });
    }

    if (result.isLockedOut) {
      return setLoginData({ status: 'locked', token: '' });
    }

    console.log(loginUser.email)
    console.log(loginUser.senha)

    return setLoginData({ status: 'error', token: '' });
  };

  return (
    <LoginContext.Provider value={{ loginData, handleLogin }}>
      {children}
    </LoginContext.Provider>
  );
};

export { LoginContext, LoginProvider };

3. 修改Private组件,添加路由跳转

使用Navigate组件实现未登录时的URL跳转:

import React from 'react';
import { Navigate } from 'react-router-dom';
import useAuth from '../hooks/useAuth';

const Private = ({ children }) => {
  const isAuthenticated = useAuth();

  return isAuthenticated ? children : <Navigate to="/" replace />;
};

export { Private };

4. 调整路由顺序,修正配置

将通配符路由放在最后,私有路由用Private组件包裹:

import React from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import SignIn from '../pages/SignIn';
import Home from '../pages/Home'
import CadastroEmpresa from '../pages/CadastroEmpresa';
import CadastroIntegrante from '../pages/CadastroIntegrante';
import Empresas from '../pages/Empresas';
import { Private } from './Private';

const RoutesApp = () => {
  return (
    <BrowserRouter>
      <Routes>
        {/* 登录页 */}
        <Route path='/' element={<SignIn />} />
        
        {/* 私有路由 */}
        <Route path='/home' element={<Private><Home /></Private>} />
        <Route path='/empresas' element={<Private><Empresas /></Private>} />
        <Route path='/cadastro-empresa' element={<Private><CadastroEmpresa /></Private>} />
        <Route path='/cadastro-funcionario' element={<Private><CadastroIntegrante /></Private>} />
        
        {/* 通配符路由:未匹配路径跳转到登录页 */}
        <Route path="*" element={<Navigate to="/" replace />} />
      </Routes>
    </BrowserRouter>
  )
}

export default RoutesApp;

关键说明

  • 登录状态通过token和status双重验证,确保只有成功获取有效token的用户能访问私有页面。
  • 使用Navigate组件替换直接渲染SignIn,保证URL正确跳转,避免刷新页面后路径异常。
  • 路由顺序遵循“精确路由优先,通配符路由最后”原则,确保私有路由能被正确匹配。

内容的提问来源于stack exchange,提问作者Douglas Badarô

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 23:30:45