Предотвращение маршрутизации в Реагировании, когда пользователь вручную изменяет URL-адрес на вкладке браузера

Я застрял в проблеме, которая возникает, когда пользователь вручную изменяет маршрут на вкладке браузера и нажимает клавишу ввода. Это заставляет мой реагирующий маршрутизатор перейти к состоянию, введенному пользователем. Я хочу предотвратить это и разрешить маршрутизацию только через поток, который я реализовал нажатием кнопки на моем веб-сайте.

Некоторые из моих экранов нуждаются в данных, которые будут доступны только в том случае, если пользователь перемещается по сайту с использованием ожидаемого потока. Если пользователь напрямую пытается перейти к определенному маршруту, вручную изменив маршрут в URL-адресе, он может пропустить нужный поток и, следовательно, приложение сломается.

Другой сценарий, если я хочу ограничить доступ некоторых пользователей к некоторым маршрутам, но пользователь знает путь и вручную вводит это в URL-адрес браузера, тогда он будет представлен с этим экраном, но не должен быть.

Ответ 1

Вы можете создать защиту маршрута с помощью HOC. Например, вы не хотите, чтобы неавторизованный пользователь прошел маршрут /profile, тогда вы можете сделать следующее:

// requireAuthorized.js (HOC)
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import {Redirect} from 'react-router-dom'

const connector = connect(
  state => ({
    isAuthorized: state.profile !== null // say, you keep user profile in redux
  })
)

export default (WrappedComponent) => {
  return (
    connector(
      class extends Component {
        static propTypes = {
          isAuthorized: PropTypes.bool.isRequired
        }

        render () {
          const {isAuthorized, ...clearedProps} = this.props
          if (isAuthorized) {
            return <WrappedComponent {...clearedProps} />
          } else {
            return <Redirect to={{pathname: '/login'}} />
          }
        }
      }
    )
  )
}


// ProfilePage.jsx
import React from 'react'
...
import requireAdmin from '../hocs/requireAdmin' // adjust path

class ProfilePage extends React.Component {
  ...
  render () {
    return (
      <div>
        ...
      </div>
    )
  }
}

export default requireAdmin(ProfilePage)

Обратите внимание на инструкцию экспорта в ProfilePage.js

Ответ 2

Что-то вроде этого подходит. Вы создаете HOC Route с оберткой для работы с реквизитами аутентификации/контекста. Примечание. Это касается прямого доступа к маршруту, а не пунктов меню и т.д. Это должно быть обработано просто по компонентам menu/menuItem.

import requireAuth from "../components/login/requireAuth";

class Routes extends React.Component<RoutesProps, {}> {
    render() {
        return (
            <div>
                <Switch>
                    <Route exact={true} path="/" component={requireAuth(Persons, ["UC52_003"])} />
                    <Route path="/jobs" component={requireAuth(Jobs, ["UC52_006"])} />
                </Switch>
            </div>
        )
    }
}


export default function (ComposedComponent, privileges) {

    interface AuthenticateProps {
        isAuthenticated: boolean
        userPrivileges: string[]
    }
    class Authenticate extends React.Component<AuthenticateProps, {}> {
        constructor(props: AuthenticateProps) {
            super(props)
        }

        render() {
            return (
                isAuthorized(this.props.isAuthenticated, privileges, this.props.userPrivileges) &&
                <ComposedComponent {...this.props} /> || <div>User is not authorised to access this page.</div>
            );
        }
    }

    function mapStateToProps(state) {
        return {
            isAuthenticated: state.userContext ? state.userContext.isAuthenticated : false,
            userPrivileges: state.userContext ? state.userContext.user ? state.userContext.user.rights : [] : []
        };
    }

    return connect(mapStateToProps, null)(Authenticate);
}

Ответ 3

Что я делаю, так это использую реквизит с предыдущей страницы, если этот реквизит не определен (имеется в виду, что пользователь не выполнил должный процесс :) хе-хе), я просто отправляю пользователя обратно на целевую страницу или куда-либо еще.

Ответ 4

Я бы предложил использовать эту библиотеку для наиболее чистого решения (или, по крайней мере, сделать ее похожую реализацию в личном плане).

Затем вы создадите проверку подлинности HOC:

export const withAuth = connectedReduxRedirect({
    redirectPath: '/login',
    authenticatedSelector: state => state.user.isAuthenticated, // or whatever you use
    authenticatingSelector: state => state.user.loading,
    wrapperDisplayName: 'UserIsAuthenticated'
});

И вы можете легко создать поток HOC:

export const withFlow = (step) = connectedReduxRedirect({
    redirectPath: '/initial-flow-step',
    authenticatedSelector: state => state.flow[step] === true,
    wrapperDisplayName: 'FlowComponent'
});

Затем инициализируйте свой компонент

const AuthenticatedComponent = withAuth(Dashboard)
const SecondStepComponent = withFlow("first-step-finished")(SecondStep)
const ThirdStepComponent = withFlow("second-step-finished")(ThirdStep)

Вы можете легко создать аутентифицированный шаг потока, составив HOC:

const AuthSecondStepComponent = withAuth(withFlow("first-step-finished")(SecondStep))

Единственное, что важно, - это то, что вы корректно обновляете свое состояние редукса в процессе выполнения шага. Когда пользователь завершит первый шаг, вы установите

state.flow["first-step-finished"] = true // or however you manage your state

так, чтобы при переходе пользователя вручную на определенную страницу у него не было такого состояния избыточности, поскольку оно находилось в памяти и было перенаправлено на маршрут redirectPath.