it-swarm.com.de

Wie implementiere ich authentifizierte Routen in React Router 4?

Ich habe versucht, authentifizierte Routen zu implementieren, aber ich habe festgestellt, dass React Router 4 jetzt verhindert, dass dies funktioniert: 

<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>

Der Fehler ist: 

Warnung: Sie sollten <Route component> und <Route children> nicht auf derselben Route verwenden. <Route children> wird ignoriert

Was ist dann der richtige Weg, dies umzusetzen? 

Es erscheint in react-router (v4) -Dokumenten 

<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>

Aber ist es möglich, dies zu erreichen, indem Sie mehrere Routen zusammenfassen? 


UPDATE

Ok, nach einigen Recherchen kam ich dazu: 

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
}

Ist es richtig, eine Aktion in render() abzusetzen, fühlt es sich falsch an. Es scheint nicht wirklich mit componentDidMount oder einem anderen Hook richtig? 

72
Jiew Meng

Sie möchten die Redirect-Komponente verwenden. Es gibt verschiedene Ansätze für dieses Problem. Hier ist eine, die ich mag, eine PrivateRoute-Komponente haben, die eine authed-Eigenschaft aufnimmt und dann auf der Grundlage dieser Eigenschaften darstellt.

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

Jetzt können Ihre Routes so aussehen

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

Wenn Sie immer noch verwirrt sind, habe ich diesen Beitrag geschrieben, der helfen könnte - Geschützte Routen und Authentifizierung mit React Router v4

147
Tyler McGinnis

Tnx Tyler McGinnis für Lösung ... Ich mache meine Idee von Tyler McGinnis Idee.

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

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

Das können Sie so umsetzen

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

decisionFunc ist nur eine Funktion, die wahr oder falsch zurückgibt

const redirectStart = props => <Redirect to="/orders" />
14
MrDuDuDu

installieren Sie den Reakt-Router-Dom

erstellen Sie dann zwei Komponenten, eine für gültige Benutzer und eine für ungültige Benutzer.

versuchen Sie es mit 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;
4
Jose G Varanam

Ich füge einfach meine Lösung zum Problem hinzu.

Ich verwende jwt-Token für die Authentifizierung. Wenn also der Benutzer dieses Token hat, leite ich sie zur Startseite oder leite sie zur Standard-Anmeldeseite weiter (dies ist diese Route '/'). Sobald der Benutzer angemeldet war und versucht, auf die Anmeldeseiten-URL zuzugreifen (in meinem Fall '/'). Ich werde sie standardmäßig nach Hause umleiten ('/ home').

Und meine Komponenten haben den HOC-Namen „requiredAuth“, um zu überprüfen, ob das Benutzertoken gültig ist. Wenn dies nicht der Fall ist, rufen Sie die Abhebeaktion auf, durch die das lokale Historientoken entfernt wird.

import React, { Component, Fragment } from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter, Route, Switch, Redirect  } from 'react-router-dom';  

//and also import appropriate components

//middleware

  class checkStatus extends React.Component {
        render() {
              if(localStorage.getItem('token')){
                return (
                  <Fragment>
                    <App>
                      <Route path="/home" exact component={Overview} />
                      <Route path="/home/add" exact component={Add} />
                      <Route path="/signout" component={Signout} />
                      <Route path="/details" component={details} />
                      <Route exact path="/" render={() => <Redirect to="/home" />} />
                    </App>

                </Fragment>
                )
              }else{
                return (
                  <Fragment>
                    <Route path="/" exact component={Signin} />
                    <Redirect to="/"  />
                  </Fragment>
                )
              }
         } }

    ReactDOM.render(   <Provider store={store}>
        <BrowserRouter>
          <Switch >
              <Route path="/" exact component={checkStatus} />
              <Route path="/:someParam"  component={checkStatus}/>
          </Switch >
        </BrowserRouter>   </Provider>,   document.querySelector('#root')
);
2
Hemanthvrm

Ich weiß, dass es schon eine Weile her ist, aber ich habe an einem npm-Paket gearbeitet für private und öffentliche Routen.

So erstellen Sie eine private Route:

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

Sie können auch öffentliche Routen erstellen, auf die nur unbefugte Benutzer zugreifen können

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

Ich hoffe, es hilft!

2
Gonzalo Cañada

Es scheint, Ihr Zögern besteht darin, Ihre eigene Komponente zu erstellen und dann die Rendermethode zu verwenden. Sie können beides vermeiden, indem Sie einfach die render-Methode der <Route>-Komponente verwenden. Sie müssen keine <AuthenticatedRoute>-Komponente erstellen, es sei denn, Sie möchten dies wirklich. Es kann so einfach sein wie unten. Beachten Sie die {...routeProps}-Spanne. Stellen Sie sicher, dass Sie die Eigenschaften der <Route>-Komponente weiterhin an die untergeordnete Komponente (in diesem Fall <MyComponent>) weiterleiten.

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

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

} />

Siehe die Dokumentation zu React Router V4

Wenn Sie eine dedizierte Komponente erstellen wollten, sieht es so aus, als wären Sie auf dem richtigen Weg. Da der React Router V4 ein rein deklaratives Routing ist (in der Beschreibung heißt es so richtig), glaube ich nicht, dass Sie Ihren Weiterleitungscode außerhalb des normalen Komponentenlebenszyklus platzieren werden. Betrachtet man den Code für den React Router selbst , führt er die Umleitung entweder in componentWillMount oder in componentDidMount durch, je nachdem, ob es sich um serverseitiges Rendering handelt oder nicht. Hier ist der Code, der ziemlich einfach ist und Ihnen helfen kann, sich wohler mit der Umleitungslogik zu fühlen.

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
1
Todd Chaffee

Basierend auf der Antwort von @Tyler McGinnis . Ich habe einen anderen Ansatz mit der ES6-Syntax und verschachtelten Routen mit umschlossenen Komponenten verwendet:

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

Und es benutzen:

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

    <Route exact path='/' component={PublicHomePage} />
  </div>
</BrowserRouter>
1
Felipe Augusto

Ich suchte auch nach einer Antwort. Hier sind alle Antworten ganz gut, aber keine gibt Antworten, wie wir sie verwenden können, wenn der Benutzer die Anwendung nach dem Öffnen wieder startet. (Ich wollte sagen, mit Cookie zusammen).

Sie müssen nicht einmal eine andere privateRoute-Komponente erstellen. Unten ist mein Code

    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;

Und hier ist 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)
}

Unten habe ich einen Blog geschrieben, dort gibt es auch genauere Erklärungen.

Geschützte Routen in ReactJS erstellen

0
nirmal

Meine vorherige Antwort ist nicht skalierbar. Ich denke, das ist ein guter Ansatz.

Ihre Routen-

<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>

Die Idee ist die Verwendung eines Wrappers in component-Requisiten, der die ursprüngliche Komponente zurückgibt, wenn keine Authentifizierung erforderlich ist oder bereits authentifiziert ist. Andernfalls würde die Standardkomponente zurückgegeben, z. Anmeldung.

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
  };
};
0
Varun Kumar

Ich implementierte using-

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

authentifizierte Requisiten werden an Komponenten weitergegeben, z. Anmeldung mit welchem ​​Benutzerstatus geändert werden kann. Komplette 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>
        );
    }
} 

Überprüfen Sie das vollständige Projekt hier: https://github.com/varunon9/hello-react

0
Varun Kumar
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>
  )
}
0
Fellow Stranger

Hier ist die einfache saubere geschützte Route

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 ist ein Methodenaufruf zum Überprüfen des Berechtigungs-Tokens, der grundsätzlich einen Booleschen Wert zurückgibt.

0
Anupam Maurya