Как ограничить доступ к маршрутам в маршрутизаторе реакции?
Кто-нибудь знает, как ограничить доступ к определенным маршрутам в ответном маршрутизаторе? Я хочу проверить, зарегистрирован ли пользователь, прежде чем разрешить доступ к определенному маршруту. Я думал, что это будет просто, но документы не ясны, как это сделать.
Я должен это настроить, где я определяю свои компоненты <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>
Ответы
Ответ 1
Обновление (16 августа 2019 г.)
В реакции-маршрутизаторе 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.
Старая версия
Принятый ответ правильный, но миксины считаются вредными (https://facebook.github.io/react/blog/2016/07/13/mixins-considered-harmful.html) командой React.
Если кто-то сталкивается с этим вопросом и ищет рекомендуемый способ сделать это, я бы предложил использовать компоненты высшего порядка вместо миксинов.
Вот пример 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
Ответ 2
Есть (сейчас?) Пример этого в документах React Router 4 для Redirect
import { Route, Redirect } from 'react-router'
<Route exact path="/" render={() => (
loggedIn ? (
<Redirect to="/dashboard"/>
) : (
<PublicHomePage/>
)
)}/>
Ответ 3
Если вы хотите использовать аутентификацию во всем своем приложении, вам необходимо сохранить некоторые данные в масштабе всего приложения (например, токен). Вы можете настроить два React mixins, которые отвечают за управление объектом $auth
. Этот объект не должен быть доступен вне этих двух микшинов. Вот пример этого:
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
использует API-интерфейс взаимодействия (willTransitionTo
и transition.abort()
).
Ответ 4
react-router
поощряет декларативный подход к вашему маршрутизатору, вы должны сделать ваш маршрутизатор настолько глупым, насколько это возможно, и не ставьте свою логику маршрутизации в свои компоненты.
Вот как вы можете это сделать (предполагая, что вы передаете его loggedIn
prop):
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} />
];
Ответ 5
частно-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/.
Ответ 6
Вы можете использовать HOC, а auth - это переменная, вы можете изменить значение true или false (авторизация)
<Route path="/login" component={SignIn} />
<Route path="/posts" render = {() => (auth ? (<Post />) : (<Redirect to="/login" />))}/>
Ответ 7
обычно регистрируемому пользователю предоставляется токен, и он использует этот токен для любой связи с сервером. То, что мы обычно делаем, это определить корневую страницу, и все будет строиться поверх этой страницы. эта корневая страница выполняет локализацию, аутентификацию и другие конфигурации для вас.
здесь пример
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>
);
на вашей корневой странице, проверьте наличие токена null или аутентифицируйте токен с сервером, чтобы узнать, действителен ли пользователь.
надеюсь, что это поможет:)