Làm thế nào để hạn chế quyền truy cập vào các tuyến đường trong bộ định tuyến phản ứng?


81

Có ai biết cách hạn chế quyền truy cập vào các tuyến đường cụ thể trong bộ định tuyến phản ứng không? Tôi muốn kiểm tra xem người dùng đã đăng nhập chưa trước khi cho phép truy cập vào một tuyến đường cụ thể. Tôi nghĩ nó sẽ đơn giản, nhưng các tài liệu không rõ cách thực hiện.

Đây có phải là thứ mà tôi nên thiết lập nơi tôi xác định <Route>các thành phần của mình hay tôi nên xử lý nó bên trong các trình xử lý thành phần của mình?

<Route handler={App} path="/">
  <NotFoundRoute handler={NotFound} name="not-found"/>
  <DefaultRoute handler={Login} name="login"/>
  <Route handler={Todos} name="todos"/> {/* I want this to be restricted */}
</Route>

Nếu họ chưa đăng nhập, hãy chuyển hướng đến trình xử lý đăng nhập. Cũng lưu ý rằng máy khách có quyền truy cập vào tất cả JS mà nó tải, vì vậy đừng lưu trữ thông tin nhạy cảm trong đó.
Đại tá Ba mươi hai

@Tanner Semerad bạn có kho lưu trữ github nào về cách bạn đạt được điều này trong thời gian ngắn không.
jit

@jit Tôi không, xin lỗi. Câu trả lời từ miciek bên dưới là những gì tôi cần, nhưng hãy nhớ rằng đây là câu trả lời trước react-router 1.0. Tôi biết một số thứ đã thay đổi kể từ khi phiên bản 1.0 được phát hành, nhưng hầu hết đều tương tự.
Tanner Semerad

@ câu trả lời jayair là những gì tôi đang sử dụng hiện nay, và nó hoạt động tuyệt vời
Tanner Semerad

Câu trả lời:


93

Cập nhật (16 tháng 8, 2019)

Trong react-router v4 và sử dụng React Hooks, điều này trông hơi khác một chút. Hãy bắt đầu với của bạn App.js.

export default function App() {
  const [isAuthenticated, userHasAuthenticated] = useState(false);

  useEffect(() => {
    onLoad();
  }, []);

  async function onLoad() {
    try {
      await Auth.currentSession();
      userHasAuthenticated(true);
    } catch (e) {
      alert(e);
    }
  }

  return (
    <div className="App container">
      <h1>Welcome to my app</h1>
      <Switch>
        <UnauthenticatedRoute
          path="/login"
          component={Login}
          appProps={{ isAuthenticated }}
        />
        <AuthenticatedRoute
          path="/todos"
          component={Todos}
          appProps={{ isAuthenticated }}
        />
        <Route component={NotFound} />
      </Switch>
    </div>
  );
}

Chúng tôi đang sử dụng một Auththư viện để kiểm tra xem người dùng hiện đã được xác thực hay chưa. Thay thế điều này bằng chức năng kiểm tra xác thực của bạn. Nếu vậy thì chúng tôi đặt isAuthenticatedcờ thành true. Chúng tôi thực hiện việc này khi Ứng dụng của chúng tôi tải lần đầu tiên. Cũng đáng nói hơn, bạn có thể muốn thêm dấu hiệu đang tải trên ứng dụng của mình trong khi quá trình kiểm tra xác thực đang được chạy, vì vậy bạn không nhấp nháy trang đăng nhập mỗi khi làm mới trang.

Sau đó, chúng tôi chuyển cờ cho các tuyến đường của chúng tôi. Chúng tôi tạo ra hai loại tuyến đường AuthenticatedRouteUnauthenticatedRoute.

Các AuthenticatedRoute.jsngoại hình như thế này.

export default function AuthenticatedRoute({ component: C, appProps, ...rest }) {
  return (
    <Route
      {...rest}
      render={props =>
        appProps.isAuthenticated
          ? <C {...props} {...appProps} />
          : <Redirect
              to={`/login?redirect=${props.location.pathname}${props.location.search}`}
            />}
    />
  );
}

Nó kiểm tra nếu isAuthenticatedđược đặt thành true. Nếu đúng như vậy, thì nó sẽ hiển thị thành phần mong muốn. Nếu không, thì nó sẽ chuyển hướng đến trang đăng nhập.

Mặt UnauthenticatedRoute.jskhác trông như thế này.

export default ({ component: C, appProps, ...rest }) =>
  <Route
    {...rest}
    render={props =>
      !appProps.isAuthenticated
        ? <C {...props} {...appProps} />
        : <Redirect to="/" />}
  />;

Trong trường hợp này, nếu isAuthenticatedđược đặt thành false, nó sẽ hiển thị thành phần mong muốn. Và nếu nó được đặt thành true, nó sẽ đưa bạn đến trang chủ.

Bạn có thể tìm thấy các phiên bản chi tiết của điều này trên hướng dẫn của chúng tôi - https://serverless-stack.com/cha Chapter/create -a- route - that - redirects.html .

Phiên bản cũ hơn

Câu trả lời được chấp nhận là đúng nhưng Mixin được coi là có hại ( https://facebook.github.io/react/blog/2016/07/13/mixins-considered-harmful.html ) bởi nhóm React.

Nếu ai đó gặp câu hỏi này và đang tìm kiếm cách được khuyến nghị để làm điều này, tôi khuyên bạn nên sử dụng Thành phần thứ tự cao hơn thay vì Mixin.

Đây là một ví dụ về HOC sẽ kiểm tra xem người dùng đã đăng nhập hay chưa trước khi tiếp tục. Và nếu người dùng chưa đăng nhập, thì nó sẽ chuyển hướng bạn đến trang đăng nhập. Thành phần này có một chỗ dựa được gọi là isLoggedIn, về cơ bản là một cờ mà ứng dụng của bạn có thể lưu trữ để biểu thị nếu người dùng đã đăng nhập.

import React from 'react';
import { withRouter } from 'react-router';

export default function requireAuth(Component) {

  class AuthenticatedComponent extends React.Component {

    componentWillMount() {
      this.checkAuth();
    }

    checkAuth() {
      if ( ! this.props.isLoggedIn) {
        const location = this.props.location;
        const redirect = location.pathname + location.search;

        this.props.router.push(`/login?redirect=${redirect}`);
      }
    }

    render() {
      return this.props.isLoggedIn
        ? <Component { ...this.props } />
        : null;
    }

  }

  return withRouter(AuthenticatedComponent);
}

Và để sử dụng HOC này, chỉ cần quấn nó quanh các tuyến đường của bạn. Trong trường hợp ví dụ của bạn, nó sẽ là:

<Route handler={requireAuth(Todos)} name="todos"/>

Tôi đề cập đến vấn đề này và một số chủ đề khác trong hướng dẫn chi tiết từng bước tại đây - https://serverless-stack.com/cha Chapter/create -a-hoc-that-checks- auth.html


Nếu mã ban đầu của tôi đang sử dụng <Route getComponent = {myAsyncComponentGenerator}>, làm cách nào để làm cho nó hoạt động với ví dụ này?
Bran

Tôi có mã rất giống nhau, nhưng câu hỏi của tôi là, có đủ bảo mật không? Ý tôi là có thể kẻ tấn công có thể thay đổi mã được rút gọn JS để thay thế this.props.isLoggedInbằng truevà bỏ qua đăng nhập?
karim elhelawy

4
@karimelhelawy Điều đó đúng và do đó bạn cần thực thi xác thực trong API máy chủ của mình.
cbr

7
<Route handler={}/>không được dùng nữa trong v1.0, bạn nên sử dụng <Route component={} />.
Kiến thức

1
componentWillMountsẽ sớm không còn được dùng nữa. Đọc nó trong bài đăng blog trên reactjs.org . Thay vào đó, tôi sẽ đi với câu trả lời mà @jacob cung cấp.
Tom

28

Có (bây giờ?) Một ví dụ về điều này trong tài liệu của React Router 4 cho Redirect

import { Route, Redirect } from 'react-router'

<Route exact path="/" render={() => (
  loggedIn ? (
    <Redirect to="/dashboard"/>
  ) : (
    <PublicHomePage/>
  )
)}/>

Làm cách nào tôi có thể sử dụng "loggedIn" làm hàm hoặc biến ?. có thể bạn chỉ cần giải thích nó một chút
Kunvar Singh

@KunvarSingh nó có lẽ phải là một hàm vì giá trị thay đổi.
jacob

2

Nếu bạn muốn sử dụng xác thực trên toàn bộ ứng dụng của mình, bạn cần lưu trữ một số dữ liệu trên toàn ứng dụng (ví dụ: mã thông báo). Bạn có thể thiết lập hai hỗn hợp React chịu trách nhiệm quản lý $authđối tượng. Đối tượng này không nên có sẵn bên ngoài hai hỗn hợp đó. Đây là ví dụ về điều đó:

define('userManagement', function() {
    'use strict';

    var $auth = {
        isLoggedIn: function () {
            // return something, e.g. using server-stored data
        }
    };

    return {
        Authenticator: {
           login: function(username, password) {
               // modify $auth object, or call server, or both
           }
        },

        NeedsAuthenticatedUser: {
            statics: {
                willTransitionTo: function (transition) {
                    if (!$auth.isLoggedIn()) {
                        transition.abort();
                    }
                }
            }
        }
    };
});

Sau đó, bạn chỉ có thể Authenticatortrộn mixin vào các thành phần đăng nhập của mình (màn hình đăng nhập, cửa sổ bật lên đăng nhập, v.v.) và this.loginchức năng gọi khi bạn có tất cả dữ liệu cần thiết.

Điều quan trọng nhất là bảo vệ các thành phần của bạn bằng cách trộn trong NeedsAuthenticatedUsermixin. Mỗi thành phần cần người dùng xác thực sẽ phải trông như thế:

var um = require('userManagement');

var ProtectedComponent = React.createClass({
    mixins: [um.NeedsAuthenticatedUser]
    // ...
}

Lưu ý rằng NeedsAuthenticatedUsersử dụng API bộ định tuyến phản ứng ( willTransitionTotransition.abort()).


2
Mixins là một ý tưởng tồi để tiếp tục. Đọc thêm
boldnik

Một cách tốt hơn nhiều mà tôi đã tìm thấy: github.com/reactjs/react-router/tree/master/examples/auth-flow
boldnik

1
Mixin đã bị loại bỏ ở dạng ES6 và React không dùng chúng nữa.
Pier

2

react-router khuyến khích cách tiếp cận khai báo cho bộ định tuyến của bạn, bạn nên làm cho bộ định tuyến của mình càng ngu ngốc càng tốt và tránh đưa logic định tuyến của bạn vào các thành phần của bạn.

Đây là cách bạn có thể làm điều đó (giả sử bạn vượt qua nó là chỗ dựa loggedIn):

const DumbRouter = ({ loggedIn }) => (
  <Router history={history}>
    <Switch>
      {[
        !loggedIn && LoggedOutRoutes,
        loggedIn && LoggedInRouter,
        <Route component={404Route} />
      ]}
    </Switch>
  </Router>
);

const LoggedInRoutes = [
  <Route path="/" component={Profile} />
];

const LoggedOutRoutes = [
  <Route path="/" component={Login} />
];

Điều này rất đơn giản, đó là tốt. Vấn đề là bạn thường muốn nhận ra các tuyến đường giống nhau nếu bạn đã đăng xuất hoặc đăng nhập, vì vậy bạn có thể chuyển hướng đúng cách để đăng nhập nếu người dùng đã đăng xuất. Bạn thường muốn các tuyến đường giống nhau, nhưng hoạt động theo một cách khác tùy thuộc vào trạng thái đã đăng nhập. Ngoài ra, với giải pháp của bạn, bạn đang thêm trùng lặp, bằng cách tạo cùng một Tuyến đường ở 2 vị trí khác nhau nhưng khó duy trì hơn.
Rafael Porras Lucena

1

Bạn có thể sử dụng HOC và auth là một biến bạn có thể thay đổi giá trị true hoặc false nghĩa là (ủy quyền)

<Route path="/login" component={SignIn} />
<Route path="/posts" render = {() => (auth ?  (<Post />) : (<Redirect to="/login" />))}/>

0

private-route.tsx

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

interface PrivateRouteProps extends RouteProps {
  /**
   * '/login' for example.
   */
  redirectTo: string;

  /**
   * If true, won't redirect.
   * We are using a function instead of a bool, a bool does not seem to be updated
   * after having successfully authenticated.
   */
  isLogged: () => boolean;
}


export function PrivateRoute(props: PrivateRouteProps) {
  // `component: Component` is not typing, it assign the value to a new variable.
  let { isLogged, redirectTo, component: Component, ...rest }: any = props;

  // error: JSX type element Component does not have call signature or ... AVOIDED BY ADDING ANY, still work,
  // and did not find a proper way to fix it.
  return <Route {...rest} render={(props) => (
    isLogged()
      ? <Component {...props}/>
      : <Redirect to={{
        pathname: redirectTo,
        state: { from: props.location }
      }} />
  )} />;
}

Sử dụng:

        <PrivateRoute exact={true} 
                      path="/admin/" 
                      redirectTo={'/admin/login'} 
                      isLogged={this.loginService.isLogged} 
                      component={AdminDashboardPage}/>
        <Route path="/admin/login/" component={AdminLoginPage}/>

Dựa trên https://tylermcginnis.com/react-router-protected-routes-authentication/ .


-2

thường người dùng đã đăng nhập sẽ được cấp mã thông báo và sử dụng mã thông báo này cho bất kỳ giao tiếp nào với máy chủ. Những gì chúng tôi thường làm là xác định một trang gốc và mọi thứ sẽ xây dựng trên đầu trang đó. trang gốc này thực hiện bản địa hóa, xác thực và các cấu hình khác cho bạn.

đây là một ví dụ

Routes = (
    <Route path="/" handler={Root}>
        <Route name="login" handler={Login} />
        <Route name="forget" handler={ForgetPassword} />
        <Route handler={Main} >
            <Route name="overview" handler={Overview} />
            <Route name="profile" handler={Profile} />
            <DefaultRoute handler={Overview} />
        </Route>
        <DefaultRoute handler={Login} />
        <NotFoundRoute handler={NotFound} />
    </Route>
);

trên trang gốc của bạn, hãy kiểm tra mã thông báo null hoặc xác thực mã thông báo với máy chủ để xem liệu người dùng có đăng nhập hợp lệ hay không.

hi vọng điêu nay co ich :)


2
Đúng vậy, vậy làm cách nào để dừng nhập lớp "Tổng quan" nếu Auth chưa được nhập hoặc trình xử lý "Chính" trông như thế nào? Ví dụ: điều gì sẽ xảy ra nếu "Tổng quan" có phần phụ thuộc yêu cầu một ứng dụng đã xác thực để chạy? Bởi vì nó được nhập để chạy trên bộ định tuyến, tất cả các phụ thuộc của nó cũng sẽ được nhập, và do đó bạn có một ứng dụng bị hỏng phải không?
Marais Rossouw,

Điều này không trả lời các câu hỏi đã được
HermannHH
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.