จะใช้เส้นทางที่พิสูจน์ตัวตนใน React Router 4 ได้อย่างไร?


122

ฉันพยายามใช้เส้นทางที่ตรวจสอบสิทธิ์ แต่พบว่าตอนนี้ React Router 4 ป้องกันไม่ให้สิ่งนี้ทำงาน:

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

ข้อผิดพลาดคือ:

คำเตือน: คุณไม่ควรใช้<Route component>และ<Route children>อยู่ในเส้นทางเดียวกัน <Route children>จะถูกละเว้น

ในกรณีนี้วิธีที่ถูกต้องในการนำไปใช้คืออะไร?

ปรากฏในreact-routerเอกสาร (v4) ซึ่งแนะนำสิ่งต่างๆเช่น

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

แต่เป็นไปได้ไหมที่จะบรรลุเป้าหมายนี้ในขณะที่จัดกลุ่มเส้นทางเข้าด้วยกัน


UPDATE

ตกลงหลังจากการวิจัยบางอย่างฉันได้สิ่งนี้:

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
}

ถูกต้องที่จะส่งการกระทำที่render()รู้สึกผิด ดูเหมือนจะไม่ถูกต้องจริงๆกับcomponentDidMountตะขออื่น ๆ หรือไม่?


สิ่งที่ดีที่สุดที่ควรทำบน ComponetWillMount หากไม่ใช้การแสดงผลฝั่งเซิร์ฟเวอร์
mfahadi

@mfahadi ขอบคุณสำหรับข้อมูล ฉันยังไม่ได้ใช้ SSR แต่ถ้าฉันต้องการใช้ในอนาคตฉันจะเก็บไว้ในการแสดงผลหรือไม่? นอกจากนี้หากผู้ใช้ถูกเปลี่ยนเส้นทางเข้าcomponentWillMountพวกเขาจะได้เห็นผลลัพธ์ที่แสดงผลแม้เพียงเสี้ยววินาทีหรือไม่
Jiew Meng

ฉันขอโทษจริงๆที่บอกว่าไม่ได้componentWillMount()ถูกเรียกใน SSR ไม่ใช่componentDidMount()ว่าจะไม่ถูกเรียก ตามที่componentWillMount()เรียกมาก่อนrender()ดังนั้นผู้ใช้จะไม่เห็นส่วนประกอบใหม่ ๆ ดังนั้นจึงเป็นสถานที่ที่ดีที่สุดในการตรวจสอบ
mfahadi

1
คุณสามารถใช้<Redirect to="/auth"> จากเอกสารแทนการเรียกการดำเนินการจัดส่งได้
Fuzail l'Corder

คำตอบ:


239

คุณจะต้องการใช้Redirectส่วนประกอบ มีแนวทางที่แตกต่างกันเล็กน้อยสำหรับปัญหานี้ นี่คือสิ่งที่ฉันชอบมีคอมโพเนนต์ PrivateRoute ที่ใช้ในauthedprop แล้วแสดงผลตามอุปกรณ์ประกอบฉากนั้น

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

ตอนนี้ของคุณRouteจะเป็นแบบนี้

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

หากคุณยังสับสนฉันเขียนโพสต์นี้ซึ่งอาจช่วยได้ - เส้นทางที่ได้รับการป้องกันและการรับรองความถูกต้องด้วย React Router v4


2
โอ้นี้จะคล้ายกับวิธีการแก้ปัญหาของฉัน <Redirect />แต่จะใช้ <Redirect />ดูเหมือนว่าปัญหาจะใช้ไม่ได้กับ redux ในกรณีของฉัน? ฉันต้องส่งการกระทำ
Jiew Meng

3
ฉันไม่รู้ว่าทำไม แต่การเพิ่มstate: {from: props.location}}}ทำให้เกิดไฟล์maximum call stack exceeded. ฉันต้องถอดมันออก คุณช่วยอธิบายได้ไหมว่าทำไมตัวเลือกนี้ถึงมีประโยชน์ @Tyler McGinnis
martpie

@KeitIG ​​แปลกดีนะ มันมีประโยชน์เพราะมันบอกคุณว่าคุณมาจากไหน ตัวอย่างเช่นหากคุณต้องการให้ผู้ใช้ตรวจสอบสิทธิ์เมื่อตรวจสอบสิทธิ์แล้วให้นำกลับไปยังหน้าที่พยายามเข้าถึงก่อนที่คุณจะเปลี่ยนเส้นทาง
Tyler McGinnis

6
@faraz สิ่งนี้อธิบายถึง({component: Component, ...rest})ไวยากรณ์ ฉันมีคำถามเดียวกันฮ่า ๆ ! stackoverflow.com/a/43484565/6502003
protoEvangelion

2
@TylerMcGinnis จะเกิดอะไรขึ้นถ้าเราจำเป็นต้องใช้ฟังก์ชันการเรนเดอร์เพื่อส่งผ่านอุปกรณ์ประกอบฉากไปยังส่วนประกอบ?
C Bauer

16

Tnx Tyler McGinnis สำหรับการแก้ปัญหา ฉันสร้างไอเดียจากไอเดียของ Tyler McGinnis

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

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

คุณสามารถดำเนินการเช่นนี้

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

decisionFunc เป็นเพียงฟังก์ชันที่ส่งคืนจริงหรือเท็จ

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

8

(ใช้ Redux สำหรับการจัดการสถานะ)

หากผู้ใช้พยายามเข้าถึง url ใด ๆ ก่อนอื่นฉันจะตรวจสอบว่ามีโทเค็นการเข้าถึงหรือไม่หากไม่เปลี่ยนเส้นทางไปยังหน้าเข้าสู่ระบบเมื่อผู้ใช้เข้าสู่ระบบโดยใช้หน้าเข้าสู่ระบบเราจะจัดเก็บข้อมูลนั้นไว้ในที่จัดเก็บในตัวเครื่องและในสถานะ redux ของเรา (localstorage หรือ cookies.. เราคงไม่ให้หัวข้อนี้ขาดบริบทในตอนนี้)
เนื่องจากสถานะ redux เป็นอัปเดตและส่วนกำหนดค่าจะถูกแสดงผลอีกครั้ง ตอนนี้เราสามารถเข้าถึงโทเค็นได้แล้วดังนั้นเราจะเปลี่ยนเส้นทางไปที่โฮมเพจ

จัดเก็บข้อมูลเพย์โหลดการอนุญาตที่ถอดรหัสแล้วเช่นกันในสถานะ redux และส่งต่อเพื่อตอบสนองบริบท (เราไม่จำเป็นต้องใช้บริบท แต่ในการเข้าถึงการให้สิทธิ์ในส่วนประกอบลูกที่ซ้อนกันของเราทำให้ง่ายต่อการเข้าถึงจากบริบทแทนที่จะเชื่อมต่อส่วนประกอบย่อยแต่ละส่วนเข้ากับ redux) ..

ทุกเส้นทางที่ไม่ต้องการบทบาทพิเศษสามารถเข้าถึงได้โดยตรงหลังจากเข้าสู่ระบบ .. หากต้องการบทบาทเช่นผู้ดูแลระบบ (เราสร้างเส้นทางที่มีการป้องกันซึ่งตรวจสอบว่าเขาต้องการบทบาทหรือไม่หากไม่เปลี่ยนเส้นทางไปยังองค์ประกอบที่ไม่ได้รับอนุญาต)

ในทำนองเดียวกันในส่วนประกอบของคุณหากคุณต้องปิดการใช้งานปุ่มหรือบางอย่างตามบทบาท

คุณสามารถทำได้ด้วยวิธีนี้

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>

แล้วจะเกิดอะไรขึ้นถ้าผู้ใช้พยายามแทรกโทเค็นดัมมี่ในที่เก็บข้อมูลในเครื่อง เมื่อเรามีโทเค็นการเข้าถึงเราจะเปลี่ยนเส้นทางไปยังส่วนประกอบหลัก ส่วนประกอบที่บ้านของฉันจะโทรออกเพื่อดึงข้อมูลเนื่องจากโทเค็น jwt เป็นแบบจำลองการโทรที่เหลือจะส่งคืนผู้ใช้ที่ไม่ได้รับอนุญาต ดังนั้นฉันจึงโทรออกจากระบบ (ซึ่งจะล้างที่เก็บข้อมูลในเครื่องและเปลี่ยนเส้นทางไปยังหน้าเข้าสู่ระบบอีกครั้ง) หากโฮมเพจมีข้อมูลคงที่และไม่ได้ทำการเรียก API ใด ๆ (คุณควรมีการเรียกใช้ API เพื่อยืนยันโทเค็นในแบ็กเอนด์เพื่อให้คุณสามารถตรวจสอบว่าโทเค็นเป็นจริงหรือไม่ก่อนที่จะโหลดโฮมเพจ)

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];
};

ตัวอย่าง JWT TOKEN ที่ถูกถอดรหัส

{
  "authorization": {
    "roles": [
      "admin",
      "operator"
    ]
  },
  "exp": 1591733170,
  "user_id": 1,
  "orig_iat": 1591646770,
  "email": "hemanthvrm@stackoverflow",
  "username": "hemanthvrm"
}

และคุณจะจัดการกับการเข้าถึงโดยตรงได้Signinอย่างไร? หากผู้ใช้รู้ว่าเขาไม่ได้ลงชื่อเข้าใช้เขาควรมีตัวเลือกในการเข้าถึงการลงชื่อเข้าใช้โดยตรงใช่ไหม
carkod

@carkod ... โดยค่าเริ่มต้นหากเขาพยายามเข้าถึงเส้นทางใด ๆ เขาจะถูกเปลี่ยนเส้นทางไปยังหน้าลงชื่อเข้าใช้ ... (เนื่องจากเขาจะไม่ต้องใช้โทเค็น)
Hemanthvrm

@carkod .. เมื่อผู้ใช้คลิกที่ออกจากระบบมิฉะนั้นโทเค็นการรีเฟรช jwt ของฉันจะหมดอายุ .. ฉันเรียกใช้ฟังก์ชันการล็อกเอาต์โดยที่ฉันล้างข้อมูลในเครื่องและหน้าต่างรีเฟรช ... ดังนั้นที่เก็บข้อมูลในเครื่องจะไม่ต้องมีโทเค็น .. มันจะเปลี่ยนเส้นทางไปยังหน้าเข้าสู่ระบบโดยอัตโนมัติ
Hemanthvrm

ฉันมีเวอร์ชันที่ดีกว่านี้สำหรับผู้ที่ใช้ redux .. จะอัปเดตคำตอบของฉันในอีกสองสามวันนี้ขอบคุณ -
Hemanthvrm

3

ติดตั้ง react-router-dom

จากนั้นสร้างสององค์ประกอบหนึ่งสำหรับผู้ใช้ที่ถูกต้องและอื่น ๆ สำหรับผู้ใช้ไม่ถูกต้อง

ลองใช้ 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
ต่อเส้นทาง? สิ่งนี้จะไม่ปรับขนาด
Jim G.

3

ขึ้นอยู่กับคำตอบของ@Tyler McGinnis ฉันสร้างแนวทางที่แตกต่างโดยใช้ไวยากรณ์ ES6และเส้นทางที่ซ้อนกันด้วยส่วนประกอบที่ห่อ:

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

และใช้มัน:

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

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

2

ฉันรู้ว่ามันผ่านมาสักพักแล้ว แต่ฉันได้ทำแพ็คเกจ npmสำหรับเส้นทางส่วนตัวและสาธารณะ

วิธีสร้างเส้นทางส่วนตัวมีดังนี้

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

และคุณยังสามารถสร้างเส้นทางสาธารณะที่ผู้ใช้ที่ไม่ได้รับอนุญาตเท่านั้นที่สามารถเข้าถึงได้

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

ฉันหวังว่ามันจะช่วยได้!


คุณช่วยยกตัวอย่างเพิ่มเติมรวมถึงการนำเข้าและการห่อทั้งหมดเช่นใน 2 รูทสาธารณะ 2 เส้นทางส่วนตัวและ 2 PropsRoute ใน App.js หลักได้ไหม ขอบคุณ
MH

2

ฉันใช้งานโดยใช้ -

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

อุปกรณ์ตรวจสอบความถูกต้องจะถูกส่งผ่านไปยังส่วนประกอบต่างๆเช่นการลงทะเบียนโดยใช้สถานะผู้ใช้ที่สามารถเปลี่ยนแปลงได้ 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>
        );
    }
} 

ตรวจสอบโครงการทั้งหมดที่นี่: https://github.com/varunon9/hello-react


1

ดูเหมือนว่าคุณลังเลในการสร้างส่วนประกอบของคุณเองแล้วส่งไปในวิธีการเรนเดอร์? คุณสามารถหลีกเลี่ยงทั้งสองอย่างได้โดยใช้renderวิธีการของ<Route>ส่วนประกอบ ไม่จำเป็นต้องสร้าง<AuthenticatedRoute>ส่วนประกอบเว้นแต่คุณต้องการจริงๆ สามารถทำได้ง่ายๆดังนี้ สังเกตการ{...routeProps}แพร่กระจายให้แน่ใจว่าคุณยังคงส่งคุณสมบัติของ<Route>คอมโพเนนต์ไปยังคอมโพเนนต์ลูก ( <MyComponent>ในกรณีนี้)

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

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

} />

ดูเอกสารReact Router V4 render

หากคุณไม่ต้องการสร้างส่วนประกอบเฉพาะดูเหมือนว่าคุณมาถูกทางแล้ว เนื่องจาก React Router V4 เป็นการกำหนดเส้นทางที่ประกาศอย่างหมดจด (ระบุไว้อย่างถูกต้องในคำอธิบาย) ฉันไม่คิดว่าคุณจะหนีไปกับการใส่รหัสเปลี่ยนเส้นทางนอกวงจรการใช้งานของส่วนประกอบปกติ เมื่อดูที่โค้ดสำหรับ React Routerพวกเขาทำการเปลี่ยนเส้นทางในแบบใดแบบหนึ่งcomponentWillMountหรือcomponentDidMountขึ้นอยู่กับว่าเป็นการแสดงผลฝั่งเซิร์ฟเวอร์หรือไม่ นี่คือโค้ดด้านล่างซึ่งค่อนข้างง่ายและอาจช่วยให้คุณรู้สึกสบายใจมากขึ้นว่าจะวางตรรกะการเปลี่ยนเส้นทางของคุณไว้ที่ใด

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

คำตอบก่อนหน้าของฉันไม่สามารถปรับขนาดได้ นี่คือสิ่งที่ฉันคิดว่าเป็นแนวทางที่ดี -

เส้นทางของคุณ -

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

แนวคิดคือการใช้กระดาษห่อหุ้มในcomponentอุปกรณ์ประกอบฉากซึ่งจะส่งคืนส่วนประกอบดั้งเดิมหากไม่จำเป็นต้องมีการตรวจสอบสิทธิ์หรือได้รับการรับรองความถูกต้องแล้วมิฉะนั้นจะส่งคืนองค์ประกอบเริ่มต้นเช่นการเข้าสู่ระบบ

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

หากไม่จำเป็นต้องมีการตรวจสอบความถูกต้องอย่าส่งผ่านองค์ประกอบไปยังฟังก์ชัน matchStateToProps ซึ่งคุณจะไม่จำเป็นต้องใช้แฟล็ก routeOpen
Dheeraj

1

นี่คือเส้นทางป้องกันที่เรียบง่ายสะอาด

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 เป็นวิธีการเรียกใช้เพื่อตรวจสอบโทเค็นการอนุญาตโดยทั่วไปจะส่งคืนบูลีน


นี่เป็นวิธีแก้ปัญหาเดียวที่ฉันพบว่าใช้งานได้ถ้าคุณส่ง Component หรือ Children ไปยังเส้นทาง
Shawn

หมายเหตุ: ฉันเพิ่งเรียก isTokenVerified () ของฉันใน ProtectedRoute funciton ของฉันและฉันไม่จำเป็นต้องส่งเสา isAllowed ในทุกเส้นทาง
Shawn

1

นี่คือวิธีที่ฉันแก้ไขด้วย React และ typescript หวังว่าจะช่วยได้!

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


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

0

ฉันก็กำลังมองหาคำตอบ ที่นี่คำตอบทั้งหมดค่อนข้างดี แต่ไม่มีคำตอบใดให้คำตอบว่าเราจะใช้มันได้อย่างไรหากผู้ใช้เริ่มแอปพลิเคชันหลังจากเปิดกลับมา (ฉันหมายถึงการใช้คุกกี้ร่วมกัน)

ไม่จำเป็นต้องสร้าง privateRoute Component ที่แตกต่างกัน ด้านล่างนี้คือรหัสของฉัน

    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;

และนี่คือ 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)
}

ด้านล่างฉันได้เขียนบล็อกคุณสามารถรับคำอธิบายเชิงลึกเพิ่มเติมได้เช่นกัน

สร้างเส้นทางที่ป้องกันใน ReactJS


0

โซลูชันที่ทำงานได้ดีที่สุดสำหรับองค์กรของฉันในที่สุดมีรายละเอียดด้านล่างเพียงเพิ่มการตรวจสอบการแสดงผลสำหรับเส้นทาง sysadmin และเปลี่ยนเส้นทางผู้ใช้ไปยังเส้นทางหลักอื่นของแอปพลิเคชันหากไม่ได้รับอนุญาตให้อยู่ในหน้า

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} />);
}

มี 3 เส้นทางหลักสำหรับการใช้งานของเราคือการหันหน้าไปทางสาธารณะ / ไซต์ไคลเอนต์ / แอปที่ล็อกอินและเครื่องมือผู้ดูแลระบบ sys ที่ / sysadmin คุณได้รับการเปลี่ยนเส้นทางตาม 'ความเป็นอิสระ' ของคุณและนี่คือหน้าเว็บที่ / sysadmin

SysAdminNav.tsx

<Switch>
    <SysAdminRoute exact path={sysadminUrls.someSysAdminUrl} render={() => <SomeSysAdminUrl/> } />
    //etc
</Switch>
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.