【问题标题】:How can I protect a route in React router如何保护 React 路由器中的路由
【发布时间】:2020-11-11 14:25:18
【问题描述】:

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

就我而言,我想保护 Notes 路线, 我正在尝试的方法根本不起作用......

我的反应代码

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);
    }
};

【问题讨论】:

标签: javascript node.js reactjs express passport.js


【解决方案1】:

考虑显示加载器,直到 api 调用返回一个值。一旦 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。

【讨论】:

  • 好的,我正在考虑实现这个,但在此之前你认为我应该使用 JWT 授权而不是这个吗?因为我不确定我的做法是否有标准。
  • 我的意思是我想知道您是否必须为您的项目进行身份验证,那么您将使用什么方法?
  • @YousufKalim,JWT 是一个很好的开始。它广泛用于用户身份验证。您可以参考 this 博客文章了解有关 JWT 的更多信息。这将帮助您在用户登录一次后保持用户身份验证状态。
【解决方案2】:

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

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;

希望这个例子对你有用。

【讨论】:

  • 是的,这很好,但只是第一次,当我刷新页面时,状态的值会自动更改为 false,
  • 在刷新再次运行 => componentDidMount 然后它会再次登录!,如果你希望用户保持登录状态,你可以实现不同的逻辑......我不知道你的后端是如何工作的,但是通常服务器会返回一个令牌,您可以将其保存在前端并检查令牌是否仍然有效,而无需每次都调用服务器!如果你的服务器遵循这个逻辑,你可以使用 redux 或 hooks 来获得更好的性能。
  • 你能给我推荐一篇关于令牌实现的文章或教程吗?
【解决方案3】:

您的函数checkAuth 因为它进行网络调用可能无法及时返回正确的值以进行渲染,所以您应该做的是创建用户是否经过身份验证的状态并让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()
},[])

【讨论】:

  • 这是有效的,但只是第一次,当我刷新页面然后自动状态的值更改为 false 并重定向到登录
  • 那么也许你需要检查你的服务器 m8,它可能没有让用户保持登录状态;同样在您的登录页面中,您需要检查用户是否经过身份验证(类似过程),如果是,则重定向到主页,这完全取决于您如何构建应用程序,考虑何时加载以及您会得到什么通过它,但这超出了这个问题的范围,不能为你编写整个应用程序m8
【解决方案4】:

替换下面的代码

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

请使用此代码

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

【讨论】:

  • 然后显示空白页,意味着'一个应用程序组件甚至没有检测到这条路线
  • 我没有添加上面提到的@steff_bdh 部分。它应该与 useState 和 useEffect 挂钩
  • 这是工作,但只是第一次,当我刷新页面然后自动状态的值更改为 false 并重定向到登录
  • 您可以通过在本地存储中存储一个值来保持登录状态
  • 例如;在 checkAuth() 函数中,您可以首先检查本地存储值。如果设置为 true,那么您可以将身份验证状态设置为 true。
猜你喜欢
  • 1970-01-01
  • 2020-10-26
  • 2019-05-31
  • 2021-02-13
  • 2021-11-03
  • 2018-01-07
  • 1970-01-01
  • 2021-04-30
  • 2016-06-07
相关资源
最近更新 更多