Jak zaimplementować uwierzytelnione trasy w React Router 4?

122

Próbowałem zaimplementować uwierzytelnione trasy, ale okazało się, że React Router 4 teraz uniemożliwia to działanie:

<Route exact path="/" component={Index} />
<Route path="/auth" component={UnauthenticatedWrapper}>
    <Route path="/auth/login" component={LoginBotBot} />
</Route>
<Route path="/domains" component={AuthenticatedWrapper}>
    <Route exact path="/domains" component={DomainsIndex} />
</Route>

Błąd:

Ostrzeżenie: nie należy używać <Route component>i <Route children>na tej samej trasie; <Route children>zostanie zignorowany

W takim razie, jaki jest właściwy sposób realizacji tego?

Pojawia się w react-routerdokumentach (v4), sugeruje coś takiego

<Router>
    <div>
    <AuthButton/>
    <ul>
        <li><Link to="/public">Public Page</Link></li>
        <li><Link to="/protected">Protected Page</Link></li>
    </ul>
    <Route path="/public" component={Public}/>
    <Route path="/login" component={Login}/>
    <PrivateRoute path="/protected" component={Protected}/>
    </div>
</Router>

Ale czy można to osiągnąć, grupując razem kilka tras?


AKTUALIZACJA

Ok, po kilku badaniach wpadłem na to:

import React, {PropTypes} from "react"
import {Route} from "react-router-dom"

export default class AuthenticatedRoute extends React.Component {
  render() {
    if (!this.props.isLoggedIn) {
      this.props.redirectToLogin()
      return null
    }
    return <Route {...this.props} />
  }
}

AuthenticatedRoute.propTypes = {
  isLoggedIn: PropTypes.bool.isRequired,
  component: PropTypes.element,
  redirectToLogin: PropTypes.func.isRequired
}

Właściwe jest wysyłanie w render()nim akcji , wydaje się złe. To naprawdę nie wydaje się poprawne z componentDidMountjakimś innym hakiem?

Jiew Meng
źródło
Najlepiej zrobić na componentWillMount, jeśli nie używasz renderowania po stronie serwera.
mfahadi
@mfahadi, dziękuję za wkład. Nie używam jeszcze SSR, ale jeśli chcę używać go w przyszłości, czy zachowam go w renderowaniu? Ponadto, jeśli użytkownik zostanie przekierowany componentWillMount, czy kiedykolwiek będzie mógł zobaczyć renderowane wyjście nawet przez ułamek sekundy?
Jiew Meng
Naprawdę przepraszam, że mówię, że componentWillMount()to nie jest wezwane do SSR, to jest to, componentDidMount()że nie jest wezwane. jak componentWillMount()nazywano wcześniej render(), więc użytkownik nie zobaczy niczego nowego komponentu. więc jest to najlepsze miejsce do sprawdzenia.
mfahadi
1
możesz po prostu użyć <Redirect to="/auth"> z dokumentacji zamiast wywoływać akcję wysyłki
Fuzail l'Corder

Odpowiedzi:

239

Będziesz chciał użyć Redirectkomponentu. Istnieje kilka różnych podejść do tego problemu. Oto jeden, który mi się podoba, mam komponent PrivateRoute, który pobiera authedprop, a następnie renderuje na podstawie tych rekwizytów.

function PrivateRoute ({component: Component, authed, ...rest}) {
  return (
    <Route
      {...rest}
      render={(props) => authed === true
        ? <Component {...props} />
        : <Redirect to={{pathname: '/login', state: {from: props.location}}} />}
    />
  )
}

Teraz twoje Routes mogą wyglądać mniej więcej tak

<Route path='/' exact component={Home} />
<Route path='/login' component={Login} />
<Route path='/register' component={Register} />
<PrivateRoute authed={this.state.authed} path='/dashboard' component={Dashboard} />

Jeśli nadal jesteś zdezorientowany, napisałem ten post, który może pomóc - Chronione trasy i uwierzytelnianie za pomocą React Router v4

Tyler McGinnis
źródło
2
Och, to jest podobne do mojego rozwiązania, ale używa <Redirect />. Problem w tym, <Redirect />że wydaje się, że nie działa z Redux w moim przypadku? Muszę wysłać akcję
Jiew Meng
3
Nie wiem dlaczego, ale dodanie state: {from: props.location}}}spowodowało maximum call stack exceeded. Musiałem to usunąć. Czy możesz wyjaśnić, dlaczego ta opcja jest przydatna @Tyler McGinnis?
martpie
@KeitIG ​​To dziwne. Jest to przydatne, ponieważ informuje, skąd pochodzisz. Przykładem może być sytuacja, w której chcesz, aby użytkownik uwierzytelnił się, a po uwierzytelnieniu przenieś go z powrotem do strony, do której próbowali uzyskać dostęp, przed przekierowaniem.
Tyler McGinnis,
6
@faraz To wyjaśnia ({component: Component, ...rest})składnię. Miałem to samo pytanie lol! stackoverflow.com/a/43484565/6502003
protoEvangelion
2
@TylerMcGinnis A gdybyśmy musieli użyć funkcji renderowania do przekazywania właściwości do komponentu?
C Bauer
16

Tnx Tyler McGinnis za rozwiązanie. Mój pomysł zrodził się z pomysłu Tylera McGinnisa.

const DecisionRoute = ({ trueComponent, falseComponent, decisionFunc, ...rest }) => {
  return (
    <Route
      {...rest}

      render={
        decisionFunc()
          ? trueComponent
          : falseComponent
      }
    />
  )
}

Możesz to zaimplementować w ten sposób

<DecisionRoute path="/signin" exact={true}
            trueComponent={redirectStart}
            falseComponent={SignInPage}
            decisionFunc={isAuth}
          />

DecisionFunc tylko funkcja, która zwraca prawdę lub fałsz

const redirectStart = props => <Redirect to="/orders" />
MrDuDuDu
źródło
8

(Używanie Redux do zarządzania stanem)

Jeśli użytkownik spróbuje uzyskać dostęp do dowolnego adresu URL, najpierw sprawdzę, czy token dostępu jest dostępny, jeśli nie przekieruje do strony logowania. Gdy użytkownik zaloguje się za pomocą strony logowania, przechowujemy go w magazynie lokalnym, a także w naszym stanie redux. (Localstorage lub cookies… na razie trzymamy ten temat poza kontekstem).
ponieważ stan Redux jako zaktualizowany, a prywatne trasy zostaną ponownie wyrenderowane. teraz mamy token dostępu, więc przekierujemy na stronę główną.

Przechowuj również zdekodowane dane ładunku autoryzacji w stanie redux i przekaż je do kontekstu reakcji. (Nie musimy używać kontekstu, ale aby uzyskać dostęp do autoryzacji w dowolnym z naszych zagnieżdżonych komponentów podrzędnych, ułatwia to dostęp z kontekstu zamiast łączenia każdego komponentu podrzędnego z reduxem).

Wszystkie trasy, które nie wymagają specjalnych ról, można uzyskać bezpośrednio po zalogowaniu .. Jeśli potrzebna jest rola, np. Admin (stworzyliśmy chronioną trasę, która sprawdza, czy miał pożądaną rolę, jeśli nie przekierowuje do nieautoryzowanego komponentu)

podobnie w każdym komponencie, jeśli musisz wyłączyć przycisk lub coś w oparciu o rolę.

po prostu możesz to zrobić w ten sposób

const authorization = useContext(AuthContext);
const [hasAdminRole] = checkAuth({authorization, roleType:"admin"});
const [hasLeadRole] = checkAuth({authorization, roleType:"lead"});
<Button disable={!hasAdminRole} />Admin can access</Button>
<Button disable={!hasLeadRole || !hasAdminRole} />admin or lead can access</Button>

A co, jeśli użytkownik spróbuje wstawić fikcyjny token w localstorage. Ponieważ mamy token dostępu, przekierujemy do komponentu domowego. Mój komponent domowy wykona wywołanie rest w celu pobrania danych, ponieważ token jwt był fikcyjny, wywołanie rest zwróci nieautoryzowanego użytkownika. Więc wywołuję wylogowanie (co wyczyści localstorage i ponownie przekieruje do strony logowania). Jeśli strona główna ma dane statyczne i nie wykonuje żadnych wywołań API (wtedy powinieneś mieć wywołanie api token-weryfikacja w zapleczu, abyś mógł sprawdzić, czy token jest PRAWDZIWY przed załadowaniem strony głównej)

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Route, Switch } from 'react-router-dom';
import history from './utils/history';


import Store from './statemanagement/store/configureStore';
import Privateroutes from './Privateroutes';
import Logout from './components/auth/Logout';

ReactDOM.render(
  <Store>
    <Router history={history}>
      <Switch>
        <Route path="/logout" exact component={Logout} />
        <Route path="/" exact component={Privateroutes} />
        <Route path="/:someParam" component={Privateroutes} />
      </Switch>
    </Router>
  </Store>,
  document.querySelector('#root')
);

History.js

import { createBrowserHistory as history } from 'history';

export default history({});

Privateroutes.js

import React, { Fragment, useContext } from 'react';
import { Route, Switch, Redirect } from 'react-router-dom';
import { connect } from 'react-redux';
import { AuthContext, checkAuth } from './checkAuth';
import App from './components/App';
import Home from './components/home';
import Admin from './components/admin';
import Login from './components/auth/Login';
import Unauthorized from './components/Unauthorized ';
import Notfound from './components/404';

const ProtectedRoute = ({ component: Component, roleType, ...rest })=> { 
const authorization = useContext(AuthContext);
const [hasRequiredRole] = checkAuth({authorization, roleType});
return (
<Route
  {...rest}
  render={props => hasRequiredRole ? 
  <Component {...props} /> :
   <Unauthorized {...props} />  } 
/>)}; 

const Privateroutes = props => {
  const { accessToken, authorization } = props.authData;
  if (accessToken) {
    return (
      <Fragment>
       <AuthContext.Provider value={authorization}>
        <App>
          <Switch>
            <Route exact path="/" component={Home} />
            <Route path="/login" render={() => <Redirect to="/" />} />
            <Route exact path="/home" component={Home} />
            <ProtectedRoute
            exact
            path="/admin"
            component={Admin}
            roleType="admin"
          />
            <Route path="/404" component={Notfound} />
            <Route path="*" render={() => <Redirect to="/404" />} />
          </Switch>
        </App>
        </AuthContext.Provider>
      </Fragment>
    );
  } else {
    return (
      <Fragment>
        <Route exact path="/login" component={Login} />
        <Route exact path="*" render={() => <Redirect to="/login" />} />
      </Fragment>
    );
  }
};

// my user reducer sample
// const accessToken = localStorage.getItem('token')
//   ? JSON.parse(localStorage.getItem('token')).accessToken
//   : false;

// const initialState = {
//   accessToken: accessToken ? accessToken : null,
//   authorization: accessToken
//     ? jwtDecode(JSON.parse(localStorage.getItem('token')).accessToken)
//         .authorization
//     : null
// };

// export default function(state = initialState, action) {
// switch (action.type) {
// case actionTypes.FETCH_LOGIN_SUCCESS:
//   let token = {
//                  accessToken: action.payload.token
//               };
//   localStorage.setItem('token', JSON.stringify(token))
//   return {
//     ...state,
//     accessToken: action.payload.token,
//     authorization: jwtDecode(action.payload.token).authorization
//   };
//    default:
//         return state;
//    }
//    }

const mapStateToProps = state => {
  const { authData } = state.user;
  return {
    authData: authData
  };
};

export default connect(mapStateToProps)(Privateroutes);

checkAuth.js

import React from 'react';

export const AuthContext = React.createContext();

export const checkAuth = ({ authorization, roleType }) => {
  let hasRequiredRole = false;

  if (authorization.roles ) {
    let roles = authorization.roles.map(item =>
      item.toLowerCase()
    );

    hasRequiredRole = roles.includes(roleType);
  }

  return [hasRequiredRole];
};

ODKODOWANA PRÓBKA TOKENU JWT

{
  "authorization": {
    "roles": [
      "admin",
      "operator"
    ]
  },
  "exp": 1591733170,
  "user_id": 1,
  "orig_iat": 1591646770,
  "email": "hemanthvrm@stackoverflow",
  "username": "hemanthvrm"
}
Hemanthvrm
źródło
A jak radzisz sobie z bezpośrednim dostępem do Signin? Jeśli użytkownik wie, że nie jest zalogowany, powinien mieć możliwość bezpośredniego dostępu do Signin, prawda?
carkod
@carkod ... Domyślnie, jeśli spróbuje uzyskać dostęp do dowolnej trasy, zostanie przekierowany na stronę logowania ... (ponieważ nie będzie miał tokena)
Hemanthvrm
@carkod .. gdy użytkownik kliknie wylogowanie lub mój token odświeżania jwt wygaśnie .. wywołuję funkcję wylogowania, w której wyczyszczę magazyn lokalny i odświeżam okno ... stąd magazyn lokalny nie będzie miał tokena .. automatycznie przekieruje do strony logowania
Hemanthvrm
mam lepszą wersję dla tych, którzy używają redux. zaktualizuję moją odpowiedź za kilka dni .. dzięki -
Hemanthvrm
3

zainstaluj respond-router-dom

następnie utwórz dwa komponenty, jeden dla prawidłowych użytkowników, a drugi dla nieprawidłowych użytkowników.

spróbuj tego na app.js

import React from 'react';

import {
BrowserRouter as Router,
Route,
Link,
Switch,
Redirect
} from 'react-router-dom';

import ValidUser from "./pages/validUser/validUser";
import InValidUser from "./pages/invalidUser/invalidUser";
const loggedin = false;

class App extends React.Component {
 render() {
    return ( 
      <Router>
      <div>
        <Route exact path="/" render={() =>(
          loggedin ? ( <Route  component={ValidUser} />)
          : (<Route component={InValidUser} />)
        )} />

        </div>
      </Router>
    )
  }
}
export default App;
Jose G Varanam
źródło
4
Na trasę? To się nie skaluje.
Jim G.
3

Na podstawie odpowiedzi @Tyler McGinnis . Zrobiłem inne podejście, używając składni ES6 i zagnieżdżonych tras z opakowanymi komponentami:

import React, { cloneElement, Children } from 'react'
import { Route, Redirect } from 'react-router-dom'

const PrivateRoute = ({ children, authed, ...rest }) =>
  <Route
    {...rest}
    render={(props) => authed ?
      <div>
        {Children.map(children, child => cloneElement(child, { ...child.props }))}
      </div>
      :
      <Redirect to={{ pathname: '/', state: { from: props.location } }} />}
  />

export default PrivateRoute

I używając go:

<BrowserRouter>
  <div>
    <PrivateRoute path='/home' authed={auth}>
      <Navigation>
        <Route component={Home} path="/home" />
      </Navigation>
    </PrivateRoute>

    <Route exact path='/' component={PublicHomePage} />
  </div>
</BrowserRouter>
Felipe Augusto
źródło
2

Wiem, że minęło trochę czasu, ale pracowałem nad pakietem npm dla tras prywatnych i publicznych.

Oto jak utworzyć prywatną trasę:

<PrivateRoute exact path="/private" authed={true} redirectTo="/login" component={Title} text="This is a private route"/>

Możesz także utworzyć trasy publiczne, do których dostęp ma tylko nieuprawniony użytkownik

<PublicRoute exact path="/public" authed={false} redirectTo="/admin" component={Title} text="This route is for unauthed users"/>

Mam nadzieję, że to pomoże!

Gonzalo Cañada
źródło
czy możesz podać więcej przykładów, w tym wszystkie importy i zawinięcia, na przykład na 2 trasach publicznych, 2 trasach prywatnych i 2 trasach PropsRoute, w głównym pliku App.js? dziękuję
MH
2

Wdrożyłem za pomocą

<Route path='/dashboard' render={() => (
    this.state.user.isLoggedIn ? 
    (<Dashboard authenticate={this.authenticate} user={this.state.user} />) : 
    (<Redirect to="/login" />)
)} />

uwierzytelnione właściwości zostaną przekazane do komponentów, np. rejestracja, za pomocą której można zmienić stan użytkownika. Kompletne trasy AppRoutes

import React from 'react';
import { Switch, Route } from 'react-router-dom';
import { Redirect } from 'react-router';

import Home from '../pages/home';
import Login from '../pages/login';
import Signup from '../pages/signup';
import Dashboard from '../pages/dashboard';

import { config } from '../utils/Config';

export default class AppRoutes extends React.Component {

    constructor(props) {
        super(props);

        // initially assuming that user is logged out
        let user = {
            isLoggedIn: false
        }

        // if user is logged in, his details can be found from local storage
        try {
            let userJsonString = localStorage.getItem(config.localStorageKey);
            if (userJsonString) {
                user = JSON.parse(userJsonString);
            }
        } catch (exception) {
        }

        // updating the state
        this.state = {
            user: user
        };

        this.authenticate = this.authenticate.bind(this);
    }

    // this function is called on login/logout
    authenticate(user) {
        this.setState({
            user: user
        });

        // updating user's details
        localStorage.setItem(config.localStorageKey, JSON.stringify(user));
    }

    render() {
        return (
            <Switch>
                <Route exact path='/' component={Home} />
                <Route exact path='/login' render={() => <Login authenticate={this.authenticate} />} />
                <Route exact path='/signup' render={() => <Signup authenticate={this.authenticate} />} />
                <Route path='/dashboard' render={() => (
                    this.state.user.isLoggedIn ? 
                            (<Dashboard authenticate={this.authenticate} user={this.state.user} />) : 
                            (<Redirect to="/login" />)
                )} />
            </Switch>
        );
    }
} 

Sprawdź cały projekt tutaj: https://github.com/varunon9/hello-react

Varun Kumar
źródło
1

Wygląda na to, że wahasz się przy tworzeniu własnego komponentu, a następnie wysyłaniu go metodą renderowania? Cóż, możesz uniknąć obu, po prostu używając rendermetody <Route>komponentu. Nie ma potrzeby tworzenia <AuthenticatedRoute>komponentu, chyba że naprawdę chcesz. Może to być tak proste, jak poniżej. Zwróć uwagę na {...routeProps}rozkładówkę, upewniając się, że nadal wysyłasz właściwości <Route>komponentu do komponentu podrzędnego ( <MyComponent>w tym przypadku).

<Route path='/someprivatepath' render={routeProps => {

   if (!this.props.isLoggedIn) {
      this.props.redirectToLogin()
      return null
    }
    return <MyComponent {...routeProps} anotherProp={somevalue} />

} />

Zobacz dokumentację renderowania React Router V4

Jeśli chciałeś stworzyć dedykowany komponent, wygląda na to, że jesteś na dobrej drodze. Ponieważ React Router V4 jest czysto deklaratywnym routingiem (tak jest napisane w opisie), nie sądzę, że wyjdzie ci na sucho umieszczenie kodu przekierowania poza normalnym cyklem życia komponentu. Patrząc na kod samego routera React , wykonują przekierowanie w albo componentWillMountlubcomponentDidMount w zależności od tego, czy jest to strona renderowania serwera. Oto poniższy kod, który jest dość prosty i może pomóc Ci poczuć się bardziej komfortowo z miejscem, w którym należy umieścić logikę przekierowania.

import React, { PropTypes } from 'react'

/**
 * The public API for updating the location programatically
 * with a component.
 */
class Redirect extends React.Component {
  static propTypes = {
    push: PropTypes.bool,
    from: PropTypes.string,
    to: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.object
    ])
  }

  static defaultProps = {
    push: false
  }

  static contextTypes = {
    router: PropTypes.shape({
      history: PropTypes.shape({
        push: PropTypes.func.isRequired,
        replace: PropTypes.func.isRequired
      }).isRequired,
      staticContext: PropTypes.object
    }).isRequired
  }

  isStatic() {
    return this.context.router && this.context.router.staticContext
  }

  componentWillMount() {
    if (this.isStatic())
      this.perform()
  }

  componentDidMount() {
    if (!this.isStatic())
      this.perform()
  }

  perform() {
    const { history } = this.context.router
    const { push, to } = this.props

    if (push) {
      history.push(to)
    } else {
      history.replace(to)
    }
  }

  render() {
    return null
  }
}

export default Redirect
Todd Chaffee
źródło
1

Moja poprzednia odpowiedź nie jest skalowalna. Oto, co uważam za dobre podejście:

Twoje trasy

<Switch>
  <Route
    exact path="/"
    component={matchStateToProps(InitialAppState, {
      routeOpen: true // no auth is needed to access this route
    })} />
  <Route
    exact path="/profile"
    component={matchStateToProps(Profile, {
      routeOpen: false // can set it false or just omit this key
    })} />
  <Route
    exact path="/login"
    component={matchStateToProps(Login, {
      routeOpen: true
    })} />
  <Route
    exact path="/forgot-password"
    component={matchStateToProps(ForgotPassword, {
      routeOpen: true
    })} />
  <Route
    exact path="/dashboard"
    component={matchStateToProps(DashBoard)} />
</Switch>

Pomysł polega na użyciu opakowania w componentwłaściwościach, które zwróciłoby oryginalny komponent, gdyby nie było wymagane uwierzytelnianie lub zostało już uwierzytelnione, w przeciwnym razie zwróciłoby domyślny komponent, np. Login.

const matchStateToProps = function(Component, defaultProps) {
  return (props) => {
    let authRequired = true;

    if (defaultProps && defaultProps.routeOpen) {
      authRequired = false;
    }

    if (authRequired) {
      // check if loginState key exists in localStorage (Your auth logic goes here)
      if (window.localStorage.getItem(STORAGE_KEYS.LOGIN_STATE)) {
        return <Component { ...defaultProps } />; // authenticated, good to go
      } else {
        return <InitialAppState { ...defaultProps } />; // not authenticated
      }
    }
    return <Component { ...defaultProps } />; // no auth is required
  };
};
Varun Kumar
źródło
jeśli uwierzytelnianie nie jest wymagane, nie przekazuj komponentu do funkcji matchStateToProps, dzięki czemu wyeliminowałbyś potrzebę flagi routeOpen
Dheeraj
1

Oto prosta, czysta, chroniona trasa

const ProtectedRoute 
  = ({ isAllowed, ...props }) => 
     isAllowed 
     ? <Route {...props}/> 
     : <Redirect to="/authentificate"/>;
const _App = ({ lastTab, isTokenVerified })=> 
    <Switch>
      <Route exact path="/authentificate" component={Login}/>
      <ProtectedRoute 
         isAllowed={isTokenVerified} 
         exact 
         path="/secrets" 
         component={Secrets}/>
      <ProtectedRoute 
         isAllowed={isTokenVerified} 
         exact 
         path="/polices" 
         component={Polices}/>
      <ProtectedRoute 
         isAllowed={isTokenVerified} 
         exact 
         path="/grants" component={Grants}/>
      <Redirect from="/" to={lastTab}/>
    </Switch>

isTokenVerified to wywołanie metody sprawdzającej token autoryzacji, w zasadzie zwraca wartość logiczną.

Anupam Maurya
źródło
To jedyne rozwiązanie, które znalazłem, że działa, jeśli mijasz na trasie komponent lub dzieci.
Shawn
Uwaga: właśnie wywołałem moją isTokenVerified () w mojej funkcji ProtectedRoute i nie musiałem przekazywać właściwości isAllowed na wszystkich trasach.
Shawn
1

Oto, jak rozwiązałem to za pomocą React i Typescript. Mam nadzieję, że to pomoże !

import * as React from 'react';
import { Route, RouteComponentProps, RouteProps, Redirect } from 'react-router';

const PrivateRoute: React.SFC<RouteProps> = ({ component: Component, ...rest }) => {
    if (!Component) {
      return null;
    }
    const isLoggedIn = true; // Add your provider here
    return (
      <Route
        {...rest}
            render={(props: RouteComponentProps<{}>) => isLoggedIn ? (<Component {...props} />) : (<Redirect to={{ pathname: '/', state: { from: props.location } }} />)}
      />
    );
  };

export default PrivateRoute;








<PrivateRoute component={SignIn} path="/signin" />

Max_Thom
źródło
0
const Root = ({ session }) => {
  const isLoggedIn = session && session.getCurrentUser
  return (
    <Router>
      {!isLoggedIn ? (
        <Switch>
          <Route path="/signin" component={<Signin />} />
          <Redirect to="/signin" />
        </Switch>
      ) : (
        <Switch>
          <Route path="/" exact component={Home} />
          <Route path="/about" component={About} />
          <Route path="/something-else" component={SomethingElse} />
          <Redirect to="/" />
        </Switch>
      )}
    </Router>
  )
}
Kolega nieznajomy
źródło
0

Szukałem też odpowiedzi. Tutaj wszystkie odpowiedzi są całkiem dobre, ale żadna z nich nie daje odpowiedzi, jak możemy z niej skorzystać, jeśli użytkownik uruchomi aplikację po ponownym otwarciu. (Miałem na myśli wspólne używanie ciasteczek).

Nie ma potrzeby tworzenia nawet innego komponentu privateRoute. Poniżej znajduje się mój kod

    import React, { Component }  from 'react';
    import { Route, Switch, BrowserRouter, Redirect } from 'react-router-dom';
    import { Provider } from 'react-redux';
    import store from './stores';
    import requireAuth from './components/authentication/authComponent'
    import SearchComponent from './components/search/searchComponent'
    import LoginComponent from './components/login/loginComponent'
    import ExampleContainer from './containers/ExampleContainer'
    class App extends Component {
    state = {
     auth: true
    }


   componentDidMount() {
     if ( ! Cookies.get('auth')) {
       this.setState({auth:false });
     }
    }
    render() {
     return (
      <Provider store={store}>
       <BrowserRouter>
        <Switch>
         <Route exact path="/searchComponent" component={requireAuth(SearchComponent)} />
         <Route exact path="/login" component={LoginComponent} />
         <Route exact path="/" component={requireAuth(ExampleContainer)} />
         {!this.state.auth &&  <Redirect push to="/login"/> }
        </Switch>
       </BrowserRouter>
      </Provider>);
      }
     }
    }
    export default App;

A oto authComponent

import React  from 'react';
import { withRouter } from 'react-router';
import * as Cookie from "js-cookie";
export default function requireAuth(Component) {
class AuthenticatedComponent extends React.Component {
 constructor(props) {
  super(props);
  this.state = {
   auth: Cookie.get('auth')
  }
 }
 componentDidMount() {
  this.checkAuth();
 }
 checkAuth() {
  const location = this.props.location;
  const redirect = location.pathname + location.search;
  if ( ! Cookie.get('auth')) {
   this.props.history.push(`/login?redirect=${redirect}`);
  }
 }
render() {
  return Cookie.get('auth')
   ? <Component { ...this.props } />
   : null;
  }
 }
 return  withRouter(AuthenticatedComponent)
}

Poniżej napisałem bloga, możesz tam również uzyskać bardziej szczegółowe wyjaśnienia.

Twórz chronione trasy w ReactJS

nirmal
źródło
0

Rozwiązanie, które ostatecznie działało najlepiej dla mojej organizacji, jest szczegółowo opisane poniżej, po prostu dodaje sprawdzenie renderowania dla trasy sysadmin i przekierowuje użytkownika do innej głównej ścieżki aplikacji, jeśli nie mogą przebywać na stronie.

SysAdminRoute.tsx

import React from 'react';
import { Route, Redirect, RouteProps } from 'react-router-dom';
import AuthService from '../services/AuthService';
import { appSectionPageUrls } from './appSectionPageUrls';
interface IProps extends RouteProps {}
export const SysAdminRoute = (props: IProps) => {
    var authService = new AuthService();
    if (!authService.getIsSysAdmin()) { //example
        authService.logout();
        return (<Redirect to={{
            pathname: appSectionPageUrls.site //front-facing
        }} />);
    }
    return (<Route {...props} />);
}

Istnieją 3 główne trasy dla naszej implementacji, publiczna strona / witryna, zalogowany klient / aplikacja i narzędzia administratora sys w / sysadmin. Zostaniesz przekierowany na podstawie Twojej „autoryzacji” i to jest strona w / sysadmin.

SysAdminNav.tsx

<Switch>
    <SysAdminRoute exact path={sysadminUrls.someSysAdminUrl} render={() => <SomeSysAdminUrl/> } />
    //etc
</Switch>
C Bauer
źródło