如何保护 React 路由器中的路由

How can I protect a route in React router

我正在使用 React with Node & express 并使用护照和本地护照进行身份验证,我想知道如何在 React 中保护路由,我的意思是我只想在用户通过身份验证时显示组件并且否则我希望它在登录页面上重定向。

在我的例子中,我想保护 Notes 路由, 我尝试的方法根本不起作用...

我的 React 代码

import React from 'react';
import { Switch, Route, Redirect } from 'react-router-dom';
import axios from 'axios';

import Register from './Components/Register';
import Login from './Components/Login';
import Notes from './Components/Notes';


function App () {


  //Function to check authentication from server
  const checkAuth = () => {
    axios.get('http://localhost:8000/notes', { withCredentials : true })
    .then(res => res.data)
  }


  return (
    <Switch>
      <Route exact path='/' component={Register} />
      <Route exact path='/login' component={Login} />

      <Route exact path='/notes' render={() => {
         return checkAuth()? (
             <Notes />
           ) : (
             <Redirect to="/login" />
           )
      }} />
    </Switch>
  );
};

export default App;

和我的服务器端代码

//Notes Page
router.get('/notes', (req, res) => {
    if(req.isAuthenticated()) {
        res.send(req.user.email);
    }else{
        res.send(false);
    }
};

替换下面的代码

<Route exact path='/notes' render={() => {
         checkAuth()? (
             <Notes />
           ) : (
             <Redirect to="/login" />
           )
      }} />

请使用此代码

<Route exact path="/notes">{checkAuth ? <Notes /> : <Redirect to="/login" />}</Route>

通过“服务器请求”检查登录状态需要时间!然后考虑创建一个 Class with state!,我将在下面给你一个例子:

import React, { Component } from 'react';
import { Switch, Route, Redirect } from 'react-router-dom';
import axios from 'axios';

import Register from './Components/Register';
import Login from './Components/Login';
import Notes from './Components/Notes';


class App extends Component {

    state = {
        login: false
    }

    componentDidMount() {
        //Function to check authentication from server
        this.checkAuth()
    }

    checkAuth = () => {
        // API request to check if user is Authenticated
        axios.get('http://localhost:8000/notes', { withCredentials : true })
        .then( res => {
            console.log(res.data);
            // API response with data => login success!
            this.setState({
                login: true
            });
        });
    }


    render() {

        let protectedRoutes = null;
        if(this.state.login === true) {
            // if login state is true then make available this Route!
            protectedRoutes = <Route exact path='/notes' component={Notes} />
        }

        return (
            <Switch>
                <Route exact path='/' component={Register} />
                <Route exact path='/login' component={Login} />
                { protectedRoutes }
            </Switch>
        );
    }
};

export default App;

希望这个例子对你有用。

你的函数 checkAuth 因为它进行网络调用可能不会 return 正确的渲染时间值,你应该做的是创建一个状态来判断用户是否通过身份验证并且让 checkAuth 更新该状态。

const [authenticated, updateAuthenticated] = useState(false);

... 更新您的 checkAuth 以更新身份验证状态

//Function to check authentication from server
const checkAuth = () => {
    axios.get('http://localhost:8000/notes', { withCredentials : true })
    .then(res => updateAuthenticated(res.data)); // double check to ensure res.data contains the actual value returned
  }

... 更新您的渲染以使用状态

<Route exact path='/notes' render={() => {
     return (authenticated ? 
         (<Notes />)
        : 
         (<Redirect to="/login" />)
       )
  }} />

...使用这种方法,您还可以将 useState 中的默认值设置为 true 或 false,并确定是否是您的服务器端代码造成了问题

...不要在 useEffect 顶部的某处调用 checkAuth

useEffect(()=>{
    checkAuth()
},[])

考虑显示一个加载器,直到 api 调用 returns 一个值。一旦 api 调用返回一个值,然后使用更高阶的组件呈现所需的组件。

App.jsx

class App extends Component {

state = {
    login: false,
    loading: false,
}

componentDidMount() {
    //Function to check authentication from server
    this.setState( {loadnig:true}, () => {
    this.checkAuth()
    }
}

checkAuth = () => {
    // API request to check if user is Authenticated
    axios.get('http://localhost:8000/notes', { withCredentials : true })
    .then( res => {
        console.log(res.data);
        // API response with data => login success!
        this.setState({
            login: true,
            loading:false
        });
    });
    .catch( error => {
        console.error(error);
        // Handle error
        this.setState({
            loading:false
        });
    });
}


render() {
    let {login, loading} = this.state
    let protectedRoutes = null;
    
    if(loading){
      return <loader/>
    }

    return (
        <Switch>
            //use authorize HOC with three parameters: component, requiresLogin, isLoggedIn
            <Route exact path='/path' component={authorize(component, true, login)} />
            <Route exact path='/' component={Register} />
            <Route exact path='/login' component={Login} />
        </Switch>
    );
}
};

export default App;

使用高阶组件将使您能够根据用户的登录状态灵活地控制路由。

授权,jsx

export default function (componentToRender, requiresLogin, isLoggedIn) {

  class Authenticate extends React.Component<> {
  render() {
      if (requiresLogin) {
          //must be logged in to view the page
          if (!isLoggedIn) {
              // redirect to an unauthorised page or homepage
              this.props.history.push('/unauthorised');
              return;
              // or just return <unauthoriesd {...this.props} />;
          }
      } else {
          // to be shown to non loggedin users only. Like the login or signup page
          if (isLoggedIn) {
              this.props.history.push('/unauthorised');
              return;
              //or just return <home {...this.props} />;
          }
      }
      //if all the conditions are satisfied then render the intended component.
      return <componentToRender {...this.props} />;
  }

  }
  return Authenticate;
}

此外,如果您决定为路由添加更多条件,那么您可以轻松地将它们添加到 HOC。