How to handle Authentication with react-router?

后端 未结 2 1799
野的像风
野的像风 2020-12-06 15:44

Trying to make certain routes require Authentication.

I have this:

class App extends Component {
  render() {
    const menuClass = `${this.props.c         


        
相关标签:
2条回答
  • 2020-12-06 16:21

    A simple solution would be to make a HOC (High Order Component) that wraps all protected routes.

    Depending upon how nested your app is, you may want to utilize local state or redux state.

    Working example: https://codesandbox.io/s/5m2690nn6n (this uses local state)

    routes/index.js

    import React from "react";
    import { BrowserRouter, Switch, Route } from "react-router-dom";
    import Home from "../components/Home";
    import Players from "../components/Players";
    import Schedule from "../components/Schedule";
    import RequireAuth from "../components/RequireAuth";
    
    export default () => (
      <BrowserRouter>
        <RequireAuth>
          <Switch>
            <Route exact path="/" component={Home} />
            <Route exact path="/players" component={Players} />
            <Route path="/schedule" component={Schedule} />
          </Switch>
        </RequireAuth>
      </BrowserRouter>
    );
    

    components/RequireAuth.js

    import React, { Component, Fragment } from "react";
    import { withRouter } from "react-router-dom";
    import Login from "./Login";
    import Header from "./Header";
    
    class RequireAuth extends Component {
      state = { isAuthenticated: false };
    
      componentDidMount = () => {
        if (!this.state.isAuthenticated) {
          this.props.history.push("/");
        }
      };
    
      componentDidUpdate = (prevProps, prevState) => {
        if (
          this.props.location.pathname !== prevProps.location.pathname &&
          !this.state.isAuthenticated
        ) {
          this.props.history.push("/");
        }
      };
    
      isAuthed = () => this.setState({ isAuthenticated: true });
    
      unAuth = () => this.setState({ isAuthenticated: false });
    
      render = () =>
        !this.state.isAuthenticated ? (
          <Login isAuthed={this.isAuthed} />
        ) : (
          <Fragment>
            <Header unAuth={this.unAuth} />
            {this.props.children}
          </Fragment>
        );
    }
    
    export default withRouter(RequireAuth);
    

    Or, instead of wrapping routes, you can create a protected component that houses protected routes.

    Working example: https://codesandbox.io/s/yqo75n896x (uses redux instead of local state).

    routes/index.js

    import React from "react";
    import { BrowserRouter, Route, Switch } from "react-router-dom";
    import { createStore } from "redux";
    import { Provider } from "react-redux";
    import Home from "../components/Home";
    import Header from "../containers/Header";
    import Info from "../components/Info";
    import Sponsors from "../components/Sponsors";
    import Signin from "../containers/Signin";
    import RequireAuth from "../containers/RequireAuth";
    import rootReducer from "../reducers";
    
    const store = createStore(rootReducer);
    
    export default () => (
      <Provider store={store}>
        <BrowserRouter>
          <div>
            <Header />
            <Switch>
              <Route exact path="/" component={Home} />
              <Route path="/info" component={Info} />
              <Route path="/sponsors" component={Sponsors} />
              <Route path="/protected" component={RequireAuth} />
              <Route path="/signin" component={Signin} />
            </Switch>
          </div>
        </BrowserRouter>
      </Provider>
    );
    

    containers/RequireAuth.js

    import React from "react";
    import { Route, Redirect } from "react-router-dom";
    import { connect } from "react-redux";
    import ShowPlayerRoster from "../components/ShowPlayerRoster";
    import ShowPlayerStats from "../components/ShowPlayerStats";
    import Schedule from "../components/Schedule";
    
    const RequireAuth = ({ match: { path }, isAuthenticated }) =>
      !isAuthenticated ? (
        <Redirect to="/signin" />
      ) : (
        <div>
          <Route exact path={`${path}/roster`} component={ShowPlayerRoster} />
          <Route path={`${path}/roster/:id`} component={ShowPlayerStats} />
          <Route path={`${path}/schedule`} component={Schedule} />
        </div>
      );
    
    export default connect(state => ({
      isAuthenticated: state.auth.isAuthenticated
    }))(RequireAuth);
    

    You can even get more modular by creating a wrapper function. You would be able to pick and choose any route by simply wrapping over the component. I don't have a codebox example, but it would be similar to this setup.

    For example: <Route path="/blog" component={RequireAuth(Blog)} />

    0 讨论(0)
  • 2020-12-06 16:23

    You spelled Autheenticated wrong.

    Also, this is an assumption because you only provided the stack trace and not the above error, which probably says AuthenticationService.IsAutheenticated is not a function.

    0 讨论(0)
提交回复
热议问题