使用 react-router-dom 成功认证后将用户重定向到他们请求的页面

Posted

技术标签:

【中文标题】使用 react-router-dom 成功认证后将用户重定向到他们请求的页面【英文标题】:Redirecting a user to the page they requested after successful authentication with react-router-dom 【发布时间】:2020-04-12 18:50:19 【问题描述】:

我已经构建了一个公共路由组件,用于登录以在用户未通过身份验证时显示。每当未登录的用户单击受保护的路由时,他将被重定向到登录页面,他可以在其中输入凭据。我想要一种编程方式,这样如果他使用正确的凭据登录,他应该被重定向到他首先尝试访问的页面。例如,如果用户请求个人资料页面,他应该在登录后被重定向到该页面,如果用户请求设置页面,也会发生同样的情况。

目前,我只能将它们重定向到主路径/。有什么方法可以使用 Redirect 以便它知道用户请求的路径?

这是我当前的公共路由组件代码

export const PublicRoute = (
    isAuthenticated,
    component: Component,
    ...rest
: PublicRouteProps) => (
    <Route
        ...rest
        component=(props: any) => 
            console.log(props.path);
            return isAuthenticated.auth ? (
                <Redirect to='/' />
            ) : (
                <div>
                    <Component ...props />
                </div>
            );
        
    />
);
const mapStateToProps = (state: ReduxStoreState) => (
    isAuthenticated: state.isAuthenticated
);

export default connect(mapStateToProps)(PublicRoute);

【问题讨论】:

您在寻找这个“/$props.path />” 那是无效的语法。 props.path 实际上也是未定义的。 那么你从哪里得到路径? 我正在从登录成功时要呈现的组件的道具中获取路径。但是当我尝试重定向到它时,它会将 props.path 读取为未定义。 【参考方案1】:

伙计,我认为最好的方法是像这样在componentDidMount 中使用history.push 函数:

componentDidMount() 
  if(isAuthenticated.auth) 
    this.props.history.push('/profile')
  
  else 
    this.props.history.push('/login') 
  

【讨论】:

我认为您误解了 OP 的问题。您需要以某种方式记住路径,用户想要在身份验证之前访问。【参考方案2】:

你的问题不能那么容易回答。基本上你需要记住用户想要访问的路径,这样你就可以在用户成功通过身份验证后重定向到该路径。

我为您创建了一个示例here。您可以在下面找到该示例的说明和一些代码。

因此,如果用户未通过身份验证,我们将路径设置为应用状态。我会将您的 ProtectedRoute 修改为:

import  useEffect  from 'react';
import  Redirect, Route, RouteProps, useLocation  from 'react-router';

export type ProtectedRouteProps = 
  isAuthenticated: boolean;
  authenticationPath: string;
  redirectPath: string;
  setRedirectPath: (path: string) => void;
 & RouteProps;

export default function ProtectedRoute(isAuthenticated, authenticationPath, redirectPath, setRedirectPath, ...routeProps: ProtectedRouteProps) 
  const currentLocation = useLocation();

  useEffect(() => 
    if (!isAuthenticated) 
      setRedirectPath(currentLocation.pathname);
    
  , [isAuthenticated, setRedirectPath, currentLocation]);

  if(isAuthenticated && redirectPath === currentLocation.pathname) 
    return <Route ...routeProps />;
   else 
    return <Redirect to= pathname: isAuthenticated ? redirectPath : authenticationPath  />;
  
;

为了记住身份验证和重定向路径,我将基于以下模型创建一个上下文:

export type Session = 
  isAuthenticated?: boolean;
  redirectPath: string;


export const initialSession: Session = 
  redirectPath: ''
;

根据上下文看起来是这样的:

import  createContext, useContext, useState  from "react";
import  initialSession, Session  from "../models/session";

export const SessionContext = createContext<[Session, (session: Session) => void]>([initialSession, () => ]);
export const useSessionContext = () => useContext(SessionContext);

export const SessionContextProvider: React.FC = (props) => 
  const [sessionState, setSessionState] = useState(initialSession);
  const defaultSessionContext: [Session, typeof setSessionState]  = [sessionState, setSessionState];

  return (
    <SessionContext.Provider value=defaultSessionContext>
      props.children
    </SessionContext.Provider>
  );

现在您需要将此上下文提供给您的应用:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './containers/App';
import  SessionContextProvider  from './contexts/SessionContext';
import  BrowserRouter  from 'react-router-dom';

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <SessionContextProvider>
        <App />
      </SessionContextProvider>
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById('root')
);

在您的主容器中,您可以应用受保护的路由:

import ProtectedRoute,  ProtectedRouteProps  from "../components/ProtectedRoute";
import  useSessionContext  from "../contexts/SessionContext";
import  Route, Switch  from 'react-router';
import Homepage from "./Homepage";
import Dashboard from "./Dashboard";
import Protected from "./Protected";
import Login from "./Login";

export default function App() 
  const [sessionContext, updateSessionContext] = useSessionContext();

  const setRedirectPath = (path: string) => 
    updateSessionContext(...sessionContext, redirectPath: path);
  

  const defaultProtectedRouteProps: ProtectedRouteProps = 
    isAuthenticated: !!sessionContext.isAuthenticated,
    authenticationPath: '/login',
    redirectPath: sessionContext.redirectPath,
    setRedirectPath: setRedirectPath
  ;

  return (
    <div>
      <Switch>
        <Route exact=true path='/' component=Homepage />
        <ProtectedRoute ...defaultProtectedRouteProps path='/dashboard' component=Dashboard />
        <ProtectedRoute ...defaultProtectedRouteProps path='/protected' component=Protected />
        <Route path='/login' component=Login />
      </Switch>
    </div>
  );
;

2021 年 3 月更新

我已经更新了上面的答案。从外部组件设置状态时,React 抛出错误。当/ 路径不受保护时,先前的解决方案也不起作用。应该修复这个问题。

另外我创建了an example for React Router 6。

【讨论】:

如果身份验证有延迟怎么办?无论如何它都会重定向到登录页面。 为什么不将令牌保存在本地存储中,然后在受保护的路由组件中访问它以进行身份​​验证? @vikrant:这也可以,但ProtectedRoute 将不再可重复使用。 @Robin 我认为整个应用程序通常只有一种登录信息,所以所有路由都可以使用这个受保护的路由。那么在其他应用程序中作为第三方包使用时,它以何种方式不可重复使用? @vikrant 是的,在这种情况下,没有多少人反对像你提到的那样做。我不这样做,因为我不希望组件开始操纵本地存储并尝试保持组件可重用。如果你有很多组件,其中一些与本地存储混乱,那么很难有干净的代码。【参考方案3】:

在您的 App.js 中

import  connect  from 'react-redux';
import  withRouter  from 'react-router-dom';
class App extends Component 
  constructor(props) 
    super(props)
    this.state = 
      urlToRedirect: null
    
  
  componentWillReceiveProps(nextProps) 
    if ( nextProps.location !== this.props.location && 
         !this.props.isAuthenticated && 
         this.props.history.action === 'REPLACE') 
      this.setState(urlToRedirect: this.props.location.pathname);
    
  

const mapStateToProps = (state: ReduxStoreState) => (
    isAuthenticated: state.isAuthenticated
);
export default connect(mapStateToProps, null)(withRouter(App));

您可以使用 Redux 状态,然后访问该 URL,而不是使用 setState。

【讨论】:

他要求提供 TypeScript 解决方案。 将我的解决方案转换为TS解决方案并不难。 @ОлегВойтинський 这是什么答案?!难不难,他要求一个 TypeScript 解决方案......!

以上是关于使用 react-router-dom 成功认证后将用户重定向到他们请求的页面的主要内容,如果未能解决你的问题,请参考以下文章

重定向方法在 react-router-dom 中不起作用

使用 nuxt-auth 成功认证后如何获取用户信息

使用 Spring 的 AbstractAuthenticationProcessingFilter 成功认证后解决 404

Spring认证执行了两次,先成功后失败

SpringSecurity:认证成功后如何继续向 RestController 转发请求?

1 次成功认证后发生 IOS Google OAuth 错误