如何限制对 react-router 中路由的访问?
Posted
技术标签:
【中文标题】如何限制对 react-router 中路由的访问?【英文标题】:How to restrict access to routes in react-router? 【发布时间】:2015-09-14 02:13:01 【问题描述】:有谁知道如何限制对 react-router 中特定路由的访问?我想在允许访问特定路线之前检查用户是否已登录。我认为这很简单,但文档并不清楚如何做到这一点。
这是我应该在我定义 <Route>
组件的地方进行设置,还是应该在我的组件处理程序中处理它?
<Route handler=App path="/">
<NotFoundRoute handler=NotFound name="not-found"/>
<DefaultRoute handler=Login name="login"/>
<Route handler=Todos name="todos"/> /* I want this to be restricted */
</Route>
【问题讨论】:
如果他们没有登录,重定向到登录处理程序。另请注意,客户端可以访问它加载的所有 JS,因此不要在其中存储敏感信息。 @Tanner Semerad 你有任何关于你是如何实现这一目标的 github 存储库吗? @jit 我没有,抱歉。下面来自 miciek 的答案是我所需要的,但请记住,这是在 react-router 1.0 之前。我知道自 1.0 发布以来,许多事情发生了变化,但大多相似。 @jayair 的回答是我现在使用的,效果很好 【参考方案1】:通常会授予登录用户一个令牌,并使用此令牌与服务器进行任何通信。我们通常做的是定义一个根页面,并在该页面之上构建。此根页面为您进行本地化、身份验证和其他配置。
这是一个例子
Routes = (
<Route path="/" handler=Root>
<Route name="login" handler=Login />
<Route name="forget" handler=ForgetPassword />
<Route handler=Main >
<Route name="overview" handler=Overview />
<Route name="profile" handler=Profile />
<DefaultRoute handler=Overview />
</Route>
<DefaultRoute handler=Login />
<NotFoundRoute handler=NotFound />
</Route>
);
在您的根页面上,检查令牌是否为空或向服务器验证令牌以查看用户是否有效登录。
希望这会有所帮助:)
【讨论】:
对,那么如果 Auth 没有通过,或者“Main”处理程序是什么样的,我将如何停止导入“Overview”类?例如,如果“Overview”具有需要经过身份验证的应用程序才能运行的依赖项怎么办?因为它是为了在路由器上运行而导入的,所以它的所有依赖项也会被导入,因此你的应用程序损坏了,对吧? 这不能回答所提出的问题【参考方案2】:如果您想在整个应用程序中使用身份验证,则需要在应用程序范围内存储一些数据(例如令牌)。你可以设置两个 React mixin 来负责管理 $auth
对象。这个对象不应该在这两个 mixin 之外可用。这是一个例子:
define('userManagement', function()
'use strict';
var $auth =
isLoggedIn: function ()
// return something, e.g. using server-stored data
;
return
Authenticator:
login: function(username, password)
// modify $auth object, or call server, or both
,
NeedsAuthenticatedUser:
statics:
willTransitionTo: function (transition)
if (!$auth.isLoggedIn())
transition.abort();
;
);
然后您可以将Authenticator
混合到您的登录组件(登录屏幕、登录弹出窗口等)中,并在您拥有所有必要数据时调用this.login
函数。
最重要的是通过混入NeedsAuthenticatedUser
mixin 来保护您的组件。每个需要经过身份验证的用户的组件都必须如下所示:
var um = require('userManagement');
var ProtectedComponent = React.createClass(
mixins: [um.NeedsAuthenticatedUser]
// ...
请注意,NeedsAuthenticatedUser
使用 react-router API(willTransitionTo
和 transition.abort()
)。
【讨论】:
Mixins 是个坏主意。 Read more 我发现了一个更好的方法:github.com/reactjs/react-router/tree/master/examples/auth-flow Mixins 已从 ES6 中移除,React 已弃用它们。【参考方案3】:更新(2019 年 8 月 16 日)
在 react-router v4 和使用 React Hooks 中,这看起来有点不同。让我们从你的App.js
开始吧。
export default function App()
const [isAuthenticated, userHasAuthenticated] = useState(false);
useEffect(() =>
onLoad();
, []);
async function onLoad()
try
await Auth.currentSession();
userHasAuthenticated(true);
catch (e)
alert(e);
return (
<div className="App container">
<h1>Welcome to my app</h1>
<Switch>
<UnauthenticatedRoute
path="/login"
component=Login
appProps= isAuthenticated
/>
<AuthenticatedRoute
path="/todos"
component=Todos
appProps= isAuthenticated
/>
<Route component=NotFound />
</Switch>
</div>
);
我们正在使用Auth
库来检查用户当前是否已通过身份验证。将此替换为您的身份验证检查功能。如果是这样,那么我们将isAuthenticated
标志设置为true
。我们在 App 首次加载时执行此操作。另外值得一提的是,您可能希望在运行身份验证检查时在您的应用上添加一个加载标志,这样您就不会在每次刷新页面时都闪烁登录页面。
然后我们将标志传递给我们的路线。我们创建了两种类型的路由AuthenticatedRoute
和UnauthenticatedRoute
。
AuthenticatedRoute.js
看起来像这样。
export default function AuthenticatedRoute( component: C, appProps, ...rest )
return (
<Route
...rest
render=props =>
appProps.isAuthenticated
? <C ...props ...appProps />
: <Redirect
to=`/login?redirect=$props.location.pathname$props.location.search`
/>
/>
);
它检查isAuthenticated
是否设置为true
。如果是,那么它将呈现所需的组件。如果没有,那么它会重定向到登录页面。
另一方面,UnauthenticatedRoute.js
看起来像这样。
export default ( component: C, appProps, ...rest ) =>
<Route
...rest
render=props =>
!appProps.isAuthenticated
? <C ...props ...appProps />
: <Redirect to="/" />
/>;
在这种情况下,如果 isAuthenticated
设置为 false
,它将呈现所需的组件。如果设置为 true,它会将您发送到主页。
您可以在我们的指南中找到详细版本 - https://serverless-stack.com/chapters/create-a-route-that-redirects.html。
旧版本
接受的答案是正确的,但 React 团队认为 Mixins 是有害的 (https://facebook.github.io/react/blog/2016/07/13/mixins-considered-harmful.html)。
如果有人遇到这个问题并正在寻找推荐的方法来做到这一点,我建议使用高阶组件而不是 Mixins。
这是一个 HOC 示例,它将在继续之前检查用户是否已登录。如果用户未登录,它会将您重定向到登录页面。该组件采用名为isLoggedIn
的道具,基本上是您的应用程序可以存储的一个标志,用于表示用户是否已登录。
import React from 'react';
import withRouter from 'react-router';
export default function requireAuth(Component)
class AuthenticatedComponent extends React.Component
componentWillMount()
this.checkAuth();
checkAuth()
if ( ! this.props.isLoggedIn)
const location = this.props.location;
const redirect = location.pathname + location.search;
this.props.router.push(`/login?redirect=$redirect`);
render()
return this.props.isLoggedIn
? <Component ...this.props />
: null;
return withRouter(AuthenticatedComponent);
要使用这个 HOC,只需将它包裹在您的路线周围。在您的示例中,它将是:
<Route handler=requireAuth(Todos) name="todos"/>
我在这里的详细分步教程中介绍了这个和其他一些主题 - https://serverless-stack.com/chapters/create-a-hoc-that-checks-auth.html
【讨论】:
如果我的原始代码使用this.props.isLoggedIn
替换为 true
并绕过登录?
@karimelhelawy 确实如此,因此您需要在服务器的 API 中强制执行身份验证。
<Route handler=/>
在 v1.0 中已弃用,您应该使用 <Route component= />
。
componentWillMount
很快就会被弃用。 Read it in the blog post on reactjs.org。相反,我会选择@jacob 提供的答案。【参考方案4】:
react-router
鼓励为您的路由器使用声明性方法,您应该使您的路由器尽可能地笨拙,并避免将您的路由逻辑放在您的组件中。
您可以这样做(假设您将loggedIn
属性传递给它):
const DumbRouter = ( loggedIn ) => (
<Router history=history>
<Switch>
[
!loggedIn && LoggedOutRoutes,
loggedIn && LoggedInRouter,
<Route component=404Route />
]
</Switch>
</Router>
);
const LoggedInRoutes = [
<Route path="/" component=Profile />
];
const LoggedOutRoutes = [
<Route path="/" component=Login />
];
【讨论】:
这个很简单,很好。问题是,如果您已注销或登录,您通常希望识别相同的路由,因此如果用户已注销,您可以正确重定向到登录。您通常希望路由相同,但根据登录状态以不同的方式运行。此外,使用您的解决方案,您正在添加重复,通过在 2 个更难维护的不同位置创建相同的路线。【参考方案5】:(现在?)在 React Router 4 的 Redirect
文档中有一个例子
import Route, Redirect from 'react-router'
<Route exact path="/" render=() => (
loggedIn ? (
<Redirect to="/dashboard"/>
) : (
<PublicHomePage/>
)
)/>
【讨论】:
如何使用“loggedIn”作为函数或变量?你能解释一下吗 @KunvarSingh 它应该是一个函数,因为值发生了变化。【参考方案6】:private-route.tsx
import Redirect, Route, RouteProps from 'react-router';
import * as React from 'react';
interface PrivateRouteProps extends RouteProps
/**
* '/login' for example.
*/
redirectTo: string;
/**
* If true, won't redirect.
* We are using a function instead of a bool, a bool does not seem to be updated
* after having successfully authenticated.
*/
isLogged: () => boolean;
export function PrivateRoute(props: PrivateRouteProps)
// `component: Component` is not typing, it assign the value to a new variable.
let isLogged, redirectTo, component: Component, ...rest : any = props;
// error: JSX type element Component does not have call signature or ... AVOIDED BY ADDING ANY, still work,
// and did not find a proper way to fix it.
return <Route ...rest render=(props) => (
isLogged()
? <Component ...props/>
: <Redirect to=
pathname: redirectTo,
state: from: props.location
/>
) />;
用法:
<PrivateRoute exact=true
path="/admin/"
redirectTo='/admin/login'
isLogged=this.loginService.isLogged
component=AdminDashboardPage/>
<Route path="/admin/login/" component=AdminLoginPage/>
基于https://tylermcginnis.com/react-router-protected-routes-authentication/。
【讨论】:
【参考方案7】:您可以使用 HOC,而 auth 是一个变量,您可以更改值 true 或 false 表示(授权)
<Route path="/login" component=SignIn />
<Route path="/posts" render = () => (auth ? (<Post />) : (<Redirect to="/login" />))/>
【讨论】:
【参考方案8】:您可以避免在确认身份验证之前渲染组件,如下所示:
import useState, useEffect, useRef from 'react';
import useHistory from 'react-router-dom';
const Route = () =>
const [loading, sertLoading] = useState(true);
const history = useHistory();
const ref = useRef<Function>();
// must use ref!
ref.current.routeGuard = () =>
const authenticationHandler = (): boolean =>
// do authentication here
sertLoading(true);
const go = authenticationHandler();
if (go === false)
history.goBack();
sertLoading(false);
useEffect(() =>
ref.current.routeGuard();
history.listen(() =>
ref.current.routeGuard();
);
, []);
return (
<>
!loading && <YourRouteComponent />
</>
)
;
或者简单地说,yarn add react-routers
,哪个组件有 props beforeEach
,beforeRoute
像 Vue Route。
【讨论】:
以上是关于如何限制对 react-router 中路由的访问?的主要内容,如果未能解决你的问题,请参考以下文章
阻止用户访问其他路由 - ReactJS - React-Router -
通过在 react-router 的地址栏上输入它的地址直接访问路由器
如果使用 history.push 访问特定路由,React-Router v5 不会重定向