[英]How to restrict access to routes in react-router?
有誰知道如何限制對 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>
在 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
。 我們在應用程序首次加載時執行此操作。 另外值得一提的是,您可能希望在運行身份驗證檢查時在您的應用程序上添加一個加載標志,這樣您就不會在每次刷新頁面時都刷新登錄頁面。
然后我們將標志傳遞給我們的路線。 我們創建了兩種類型的路由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-thinked-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
在 React Router 4 的Redirect
文檔中有(現在?)一個例子
import { Route, Redirect } from 'react-router'
<Route exact path="/" render={() => (
loggedIn ? (
<Redirect to="/dashboard"/>
) : (
<PublicHomePage/>
)
)}/>
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} />
];
如果要在整個應用程序中使用身份驗證,則需要在應用程序范圍內存儲一些數據(例如令牌)。 您可以設置兩個負責管理$auth
對象的 React mixin。 該對象不應該在這兩個 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
混合來保護您的組件。 每個需要經過身份驗證的用戶的組件都必須如下所示:
var um = require('userManagement');
var ProtectedComponent = React.createClass({
mixins: [um.NeedsAuthenticatedUser]
// ...
}
請注意, NeedsAuthenticatedUser
使用 react-router API( willTransitionTo
和transition.abort()
)。
您可以使用 HOC 並且 auth 是一個變量,您可以更改值 true 或 false 表示(授權)
<Route path="/login" component={SignIn} />
<Route path="/posts" render = {() => (auth ? (<Post />) : (<Redirect to="/login" />))}/>
私有路由.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/ 。
您可以避免在確認身份驗證之前渲染組件,如下所示:
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
,其中組件有道具beforeEach
, beforeRoute
像 Vue Route。
通常,登錄用戶將被授予一個令牌,並使用此令牌與服務器進行任何通信。 我們通常做的是定義一個根頁面,然后建立在該頁面之上。 此根頁面為您進行本地化、身份驗證和其他配置。
這是一個例子
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>
);
在您的根頁面上,檢查令牌是否為空或使用服務器驗證令牌以查看用戶是否有效登錄。
希望這可以幫助 :)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.