使用 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);
【问题讨论】:
您在寻找这个“伙计,我认为最好的方法是像这样在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 成功认证后将用户重定向到他们请求的页面的主要内容,如果未能解决你的问题,请参考以下文章
使用 Spring 的 AbstractAuthenticationProcessingFilter 成功认证后解决 404