Защита маршрутов в приложении React с помощью React Router - PullRequest
0 голосов
/ 08 января 2020

Я создал простое приложение React с Redux, React Router и Auth0, которое обрабатывает аутентификации пользователей.

Я пытаюсь создать это базовое c поведение для контроля доступа:

  • Все неаутентифицированные пользователи будут автоматически отправлены на /public
  • Аутентифицированные пользователи может получить доступ ко всем другим частям приложения
  • Как только пользователь аутентифицируется с помощью Auth0, я хочу обработать access_token и отправить пользователя на /, который является Home компонентом

Все "почти" работает так, как должно. У меня проблема в том, что функция render() в App.jsx выполняется ДО того, как у lock.on('authenticated') слушателя даже есть шанс обработать токены, возвращаемые Auth0. В результате токены никогда не сохраняются, и пользователь всегда кажется не прошедшим аутентификацию. Если я отправляю пользователя на /login, все работает нормально, потому что я не проверяю, прошел ли аутентификация пользователя перед рендерингом компонента Login.

Я думаю, что для работы с защищенными маршрутами нужны изменить. Любые предложения о том, как обрабатывать защищенные маршруты?

Я предоставляю код, который вам нужен здесь. Если вы хотите увидеть все приложение, от go до https://github.com/imsam67/react-redux-react-router-auth0-lock

Ниже приводится App.jsx:

class App extends Component {

    render() {

        const isAuthed = isAuthenticated();

        return (
            <div>
                <Switch>
                    <Route exact path="/" render={ props => isAuthed ? <Home {...props} /> : <Redirect to="/public" /> } />
                    <Route exact path="/login">
                        <Login />
                    </Route>
                    <Route path="/public">
                        <Public />
                    </Route>
                </Switch>
            </div>
        );
    }
}

Это AuthWrapper компонент, где я обрабатываю Auth0:

class AuthWrapper extends Component {

    constructor(props) {

        super(props);
        this.onAuthenticated = this.onAuthenticated.bind(this);

        this.lock = new Auth0Lock('my_auth0_client_id', 'my_domain.auth0.com', {
            auth: {
                audience: 'https://my_backend_api_url',
                redirectUrl: 'http://localhost:3000/',
                responseType: 'token id_token',
                sso: false
            }
        });

        this.onAuthenticated();
    }

    onAuthenticated() {
        debugger; // After successful login, I hit this debugger
        this.lock.on('authenticated', (authResult) => {
            debugger; // But I never hit this debugger
            let expiresAt = JSON.stringify((authResult.expiresIn * 1000) + new Date().getTime());
            sessionStorage.setItem('access_token', authResult.accessToken);
            sessionStorage.setItem('id_token', authResult.idToken);
            sessionStorage.setItem('expires_at', expiresAt);

          });
    }

    render() {

        return(
            <AuthContext.Provider value={{ lock: this.lock }}>
                {this.props.children}
            </AuthContext.Provider>
        );
    }

}

А вот index.js на тот случай, если вам нужно его увидеть:

import App from './components/App';
import AuthWrapper from './components/auth/AuthWrapper';

// Store
import appStore from './store/app-store';
const store = appStore();

ReactDOM.render(
    <Provider store={store}>
        <BrowserRouter>
            <AuthWrapper>
                <App />
            </AuthWrapper>
        </BrowserRouter>
    </Provider>,
    document.getElementById('root')
);

Ответы [ 4 ]

0 голосов
/ 11 января 2020

Помните, что на стороне клиента вообще ничего не защищено. Если вас беспокоит маршрутизация к компоненту без аутентификации, просто убедитесь, что данные не открыты, и перенаправьте их, если они попали туда даже после того, как ваш маршрутизатор проверит аутентификацию. Я думаю, что @Sam это правильно. Маршруты могут не реагировать, как ожидается, на асинхронный вызов, меняющий его, или могут иметь странное поведение. Я никогда не пробовал динамический c маршрут таким образом, но всегда имел условные рендеры компонентов контента. Лучшим подходом может быть отправка вызова и перенаправление блока затем на URL, который маршрутизатор знает для обработки. Просто не уверен, что роутер справляется с этим очень хорошо. Поймать компонент, проверяющий аутентификацию при загрузке и перенаправить обратно, чтобы войти, если не авторизован. Извините, я здесь не особо помогаю, но условные маршруты почти кажутся анти-паттернами, но я думаю, что это могло бы работать, если бы мы знали, как маршрутизатор отображает свои данные после изменений, или если это вообще так (маршруты сами по себе). Так что если они должны были добавить URL в закладки и попытаться вернуться назад, что было бы 404 верно? Может быть лучше 401 несанкционированный показ и перенаправление или ссылка для входа в систему?

0 голосов
/ 10 января 2020

Вот изменения, которые я сделал, чтобы сделать эту работу.

  1. Теперь я инициализирую Auth0 Lock в index.js, чтобы сделать его глобальным
  2. Я переместил слушатель onAuthenticated() в LoginCallback.jsx компонент, куда пользователь отправляется после успешного входа в систему. Я считаю, что перемещение onAuthenticated() из App.jsx в LoginCallback.jsx оказало наибольшее влияние, потому что слушатель в LoginCallback.jsx выполняется до App.jsx.
  3. При успешной аутентификации я также использую this.props.history.push('/'); для отправки пользователь Home компонент

Полный код см. в репо на https://github.com/imsam67/react-redux-react-router-auth0-lock

0 голосов
/ 10 января 2020

Помните, что на стороне клиента вообще ничего не защищено. Если вы обеспокоены маршрутизацией к компоненту без аутентификации, просто убедитесь, что данные не выставлены (при условии, что они не могут получить данные без токена), и перенаправьте, если они приземлились там даже после того, как ваш маршрутизатор проверил на аутентификацию или выдает ошибку.

0 голосов
/ 09 января 2020

Динамическая c маршрутизация должна быть определена вне области действия <Switch>. Вот пример, предполагающий, что ваша функция isAuthenticated () является состоянием (Redux или wathever)

import React from "react";
import ReactDOM from "react-dom";
import { createBrowserHistory } from "history";
import { Router, Route, Switch, Redirect } from "react-router-dom";

// core components
import Admin from "layouts/Admin.js";
import SignIn from "layouts/SignIn";

const hist = createBrowserHistory();
const loggedRoutes = () => (
  <Switch>
    <Route path="/" component={SignIn} />
    <Route path="/admin" component={Admin} />
    <Redirect from="/admin" to="/admin/aboutUs/whatWeDo" />
  </Switch>
);
const routes = () => (
  <Switch>
    <Route path="/" component={SignIn} />
    <Route path="/login" component={Admin} />
    <Redirect from="/" to="/login" />
  </Switch>
);
ReactDOM.render(
  <Router history={hist}>
    {checkIfAuth? loggedRoutes():routes()}
  </Router>,
  document.getElementById("root")
);

В этом примере, если вы не авторизованы, вы будете перенаправлены на / login.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...