检查是否登录 - React Router App ES6

问题描述 投票:0回答:7

我正在使用 react-router (v2.8.1) 和 ES6 语法 编写 React.js 应用程序 (v15.3)。我无法让路由器代码拦截页面之间的所有转换以检查用户是否需要先登录。

我的顶级渲染方法非常简单(应用程序也很简单):

 render()
   {
      return (
         <Router history={hashHistory}>
            <Route path="/" component={AppMain}>
               <Route path="login" component={Login}/>
               <Route path="logout" component={Logout}/>
               <Route path="subject" component={SubjectPanel}/>
               <Route path="all" component={NotesPanel}/>
            </Route>
         </Router>
      );
   }

网络上的所有示例都使用 ES5 代码或旧版本的 React-router(早于版本 2),并且我对 mixins(已弃用)和 willTransitionTo(从未被调用)的各种尝试都失败了。

如何设置全局“拦截器功能”以强制用户在登陆他们请求的页面之前进行身份验证?

javascript authentication reactjs ecmascript-6 react-router
7个回答
35
投票

每个路由都有一个 onEnter 钩子,它在路由转换发生之前被调用。使用自定义 requireAuth 函数处理 onEnter 挂钩。

<Route path="/search" component={Search} onEnter={requireAuth} />

示例 requireAuth 如下所示。如果用户已通过身份验证,则通过 next() 进行转换。否则将路径名替换为 /login 并通过 next() 进行转换。登录还会传递当前路径名,以便登录完成后,用户将被重定向到最初请求的路径。

function requireAuth(nextState, replace, next) {
  if (!authenticated) {
    replace({
      pathname: "/login",
      state: {nextPathname: nextState.location.pathname}
    });
  }
  next();
}

7
投票

在 v4 中,您只需创建一个路由组件来检查用户是否经过身份验证并返回下一个组件,当然下一个组件可以是其他路由。

import React, { Component } from 'react';
import PropTypes from 'prop-types';

import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { Route, Redirect } from 'react-router-dom';

import AuthMiddleware from 'modules/middlewares/AuthMiddleware';

class PrivateRoute extends Component {
  static propTypes = {
    component: PropTypes.func.isRequired,
    isAuthenticated: PropTypes.bool,
    isLoggedIn: PropTypes.func.isRequired,
    isError: PropTypes.bool.isRequired
  };

  static defaultProps = {
    isAuthenticated: false
  };

  constructor(props) {
    super(props);
    if (!props.isAuthenticated) {
      setTimeout(() => {
        props.isLoggedIn();
      }, 5);
    }
  }

  componentWillMount() {
    if (this.props.isAuthenticated) {
      console.log('authenticated');
    } else {
      console.log('not authenticated');
    }
  }
  componentWillUnmount() {}

  render() {
    const { isAuthenticated, component, isError, ...rest } = this.props;
    if (isAuthenticated !== null) {
      return (
        <Route
          {...rest}
          render={props => (
            isAuthenticated ? (
              React.createElement(component, props)
            ) : (
              <Redirect
                to={{
                  pathname: isError ? '/login' : '/welcome',
                  state: { from: props.location }
                }}
              />
            )
          )}
        />
      );
    } return null;
  }

}

const mapStateToProps = (state) => {
  return {
    isAuthenticated: state.auth.isAuthenticated,
    isError: state.auth.isError
  };
};

const mapDispatchToProps = (dispatch) => {
  return bindActionCreators({
    isLoggedIn: () => AuthMiddleware.isLoggedIn()
  }, dispatch);
};

export default connect(mapStateToProps, mapDispatchToProps)(PrivateRoute);

3
投票

如果您使用的是react-router 4及更高版本,请使用渲染道具和重定向来解决此问题。参考:React-Router 中未调用 onEnter


2
投票
这个版本的 onEnter 回调终于适用于react-router(v2.8):

requireAuth(nextState, replace) { if(!this.authenticated()) // pseudocode - SYNCHRONOUS function (cannot be async without extra callback parameter to this function) replace('/login') }

解释 V1 与 v2 之间的反应路由器重定向差异的链接是

这里。相关部分引用如下:

Likewise, redirecting from an onEnter hook now also uses a location descriptor. // v1.0.x (nextState, replaceState) => replaceState(null, '/foo') (nextState, replaceState) => replaceState(null, '/foo', { the: 'query' }) // v2.0.0 (nextState, replace) => replace('/foo') (nextState, replace) => replace({ pathname: '/foo', query: { the: 'query' } })

完整代码列表如下(react-router 版本 2.8.1):

requireAuth(nextState, replace) { if(!this.authenticated()) // pseudocode - SYNCHRONOUS function (cannot be async without extra callback parameter to this function) replace('/login'); } render() { return ( <Router history={hashHistory}> <Route path="/" component={AppMain}> <Route path="login" component={Login}/> <Route path="logout" component={Logout}/> <Route path="subject" component={SubjectPanel} onEnter={this.requireAuth}/> <Route path="all" component={NotesPanel} onEnter={this.requireAuth}/> </Route> </Router> ); }
    

2
投票

这不是一个安全的解决方案 您可以尝试在每个需要
login的页面上使用useEffect钩子:

useEffect(() => { const token = localStorage.getItem('token'); if(!token) { history.push('/login'); } }
这使用来自“react-router-dom”的 useHistory 钩子

你只需要在调用它之前初始化它:

const history = useHistory();
如上所述,这不是一个安全的解决方案,而是一个简单的解决方案


1
投票
您可以通过简单地创建一个 RestrictedRoute 或 Private 组件并将您的 redux 用户身份验证状态传递给此组件来完成此操作,如果状态为 false,则在 RestrictedRoute 组件重定向中,以防真正重定向到组件。

代码:

const RestrictedRoute = ({ component: Component, authUser, auth, ...rest }) => ( <Route {...rest} render={props => auth.is_authenticated && auth.is_authorized && authUser ? ( <Component {...props} /> ) : ( <Redirect to={{ pathname: '/signin', state: { from: props.location }, }} /> ) } /> ); <Switch> <RestrictedRoute path={`${match.url}app`} authUser={authUser} auth={{ is_authenticated, is_authorized }} component={MainApp} /> <Route path='/signin' component={SignIn} /> <Switch>
    

0
投票

React v18

reacr-router V6
中。下面是我如何做到这一点的完整结构。

创建路线

routes.tsx


import { lazy } from 'react'; const Login = lazy(() => import('../pages/Authentication/Login')); const ProtectedPage = lazy(() => import('../pages/ProtectedPage')); const PublicPage = lazy(() => import('../pages/PublicPage')); const routes = [ { path: "/Login", element: <Login/>, protected: false }, { path: "/PublicPage", element: <PublicPage />, protected: false }, { path: "/ProtectedPage", element: <ProtectedPage />, protected: true } ]; export { routes };
创建

AuthRequired.tsx

组件来检查登录状态:

import { useSelector } from 'react-redux'; import { Navigate } from 'react-router-dom'; export const AuthRequired: FC<Props> = ({ children }) => { const { user } = useSelector((state: IRootState) => state); const isLoggedIn = Object.keys(user).length > 0; return isLoggedIn ? children : <Navigate to={AppRoutes.LOGIN} replace={true} />; };
在您的

createBrowserRouter

中使用它

import { createBrowserRouter } from 'react-router-dom'; import { AuthRequired } from './AuthRequired'; import { routes } from './routes'; import App from './App'; const finalRoutes = routes.map((route) => { return { ...route, element: route.protected ? ( <AuthRequired><App>{route.element}</App></AuthRequired> ) : ( <App>{route.element}</App> ), }; }); const router = createBrowserRouter(finalRoutes); export default router;
然后在您的 

main.tsx

 或文件中使用它,无论您在哪里创建根目录。

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( <React.StrictMode> <Suspense> <Provider store={store}> <RouterProvider router={router} /> </Provider> </Suspense> </React.StrictMode> );
    
© www.soinside.com 2019 - 2024. All rights reserved.