【问题标题】:Protecting routes in React app with React Router使用 React Router 保护 React 应用程序中的路由
【发布时间】:2020-04-26 10:36:51
【问题描述】:

我创建了一个简单的React 应用程序,其中包含处理用户身份验证的ReduxReact RouterAuth0

我正在尝试创建此基本行为来控制访问:

  • 所有未经身份验证的用户将自动发送到/public
  • 经过身份验证的用户可以访问应用程序的所有其他部分
  • 一旦用户通过Auth0 的身份验证,我想处理access_token 并将用户发送到/ 这是Home 组件

一切都“几乎”按应有的方式工作。我遇到的问题是App.jsx 中的render() 函数在lock.on('authenticated') 侦听器之前执行,甚至有机会处理Auth0 返回的令牌。结果,令牌永远不会被存储,并且用户似乎总是未经身份验证。如果我将用户发送到/login,一切正常,因为在呈现Login 组件之前,我不会检查用户是否经过身份验证。

我认为我处理受保护路由的方式需要改变。有关如何处理受保护路由的任何建议?

我在这里提供您需要的代码。如果你想看整个应用程序,去https://github.com/imsam67/react-redux-react-router-auth0-lock

以下是App.jsx

class App extends Component {

    render() {

        const isAuthed = isAuthenticated();

        return (
            <div>
                <Switch>
                    <Route exact path="/" render={ props => isAuthed ? <Home {...props} /> : <Redirect to="/public" /> } />
                    <Route exact path="/login">
                        <Login />
                    </Route>
                    <Route path="/public">
                        <Public />
                    </Route>
                </Switch>
            </div>
        );
    }
}

这是我处理Auth0AuthWrapper 组件:

class AuthWrapper extends Component {

    constructor(props) {

        super(props);
        this.onAuthenticated = this.onAuthenticated.bind(this);

        this.lock = new Auth0Lock('my_auth0_client_id', 'my_domain.auth0.com', {
            auth: {
                audience: 'https://my_backend_api_url',
                redirectUrl: 'http://localhost:3000/',
                responseType: 'token id_token',
                sso: false
            }
        });

        this.onAuthenticated();
    }

    onAuthenticated() {
        debugger; // After successful login, I hit this debugger
        this.lock.on('authenticated', (authResult) => {
            debugger; // But I never hit this debugger
            let expiresAt = JSON.stringify((authResult.expiresIn * 1000) + new Date().getTime());
            sessionStorage.setItem('access_token', authResult.accessToken);
            sessionStorage.setItem('id_token', authResult.idToken);
            sessionStorage.setItem('expires_at', expiresAt);

          });
    }

    render() {

        return(
            <AuthContext.Provider value={{ lock: this.lock }}>
                {this.props.children}
            </AuthContext.Provider>
        );
    }

}

这里是index.js,以防您需要查看:

import App from './components/App';
import AuthWrapper from './components/auth/AuthWrapper';

// Store
import appStore from './store/app-store';
const store = appStore();

ReactDOM.render(
    <Provider store={store}>
        <BrowserRouter>
            <AuthWrapper>
                <App />
            </AuthWrapper>
        </BrowserRouter>
    </Provider>,
    document.getElementById('root')
);

【问题讨论】:

  • 我实际上并没有看到你在哪里使用你的减速器中的“isAuthenticated”属性。在 App.jsx 中,您指的是您的 isAuthenticated() 实用程序,但由于这不是反应状态,因此如果它发生变化,它不会触发重新渲染。你需要使用你的 reducer state 属性来保护你的路由,而不是那个 helper util。
  • 我在appReducer 中改用isAuthenticated,而不是使用 util 函数,但这不起作用。我读过 Tyler McGinnis 的文章,但问题是我不想在保护区使用 /private 之类的东西。我希望根目录和它之后的任何东西都受到保护,即//projects。唯一不应该保护的路由是/public。所以我无法真正从文章中汲取灵感并将其应用到我的代码中。
  • 嘿山姆,对不起,没有工作。也许我只是瞎了眼,但我查看了您的存储库,但没有看到您在用户成功验证后在哪里调用您的操作来更新减速器。如果它在那里,你能指出来吗?
  • 抱歉,我没有更新 repo,但现在我更新了。我在AuthWrapper.jsx 下的src/components/auth 组件中的onAuthenticated() mehtod 中执行此操作。似乎真正的问题是,在onAuthenticated() 方法完全执行并存储access_token 之前,会发生到组件的路由。

标签: reactjs react-router-v4 auth0 react-router-dom


【解决方案1】:

需要在&lt;Switch&gt; 范围之外定义动态路由。这是一个示例,假设您的函数 isAuthenticated() 是一个状态(Redux 或 wathever)

import React from "react";
import ReactDOM from "react-dom";
import { createBrowserHistory } from "history";
import { Router, Route, Switch, Redirect } from "react-router-dom";

// core components
import Admin from "layouts/Admin.js";
import SignIn from "layouts/SignIn";

const hist = createBrowserHistory();
const loggedRoutes = () => (
  <Switch>
    <Route path="/" component={SignIn} />
    <Route path="/admin" component={Admin} />
    <Redirect from="/admin" to="/admin/aboutUs/whatWeDo" />
  </Switch>
);
const routes = () => (
  <Switch>
    <Route path="/" component={SignIn} />
    <Route path="/login" component={Admin} />
    <Redirect from="/" to="/login" />
  </Switch>
);
ReactDOM.render(
  <Router history={hist}>
    {checkIfAuth? loggedRoutes():routes()}
  </Router>,
  document.getElementById("root")
);

在这个例子中,如果你没有登录,你会被重定向到 /login。

【讨论】:

  • 看起来这一切都发生在index.js。请记住,我使用Auth0 进行身份验证,成功登录后代码命中lock.on('authenticated') 至关重要。这似乎是真正的问题,因为路由器在侦听器有机会处理它收到的令牌之前采取行动。
【解决方案2】:

这是我为完成这项工作所做的更改。

  1. 我现在在 index.js 中初始化 Auth0 Lock 以使其全局化
  2. 我将onAuthenticated() 侦听器移动到LoginCallback.jsx 组件,这是成功登录后发送用户的位置。我相信将onAuthenticated()App.jsx 移动到LoginCallback.jsx 产生了最大的影响,因为LoginCallback.jsx 中的侦听器在App.jsx 之前执行。
  3. 身份验证成功后,我还使用this.props.history.push('/'); 将用户发送到Home 组件

完整代码请查看https://github.com/imsam67/react-redux-react-router-auth0-lock的repo

【讨论】:

  • github 链接暂时失效!
  • 对不起,我删除了 GitHub 上的存储库。您需要对此进行任何澄清吗?您只需将 Auth0 Lock 实例化为全局对象,即 global.auth = new Auth0()window.auth = new Auth0()。从那时起,您可以通过调用window.auth 在您的应用程序中的任何位置访问此对象。 HTH
【解决方案3】:

请记住,客户端根本没有任何东西受到保护。如果您担心路由到没有身份验证的组件,只需确保没有数据暴露(假设他们无法在没有令牌的情况下获取任何数据),并且即使在您的路由器检查身份验证或显示错误之后他们登陆那里也重定向。

【讨论】:

    【解决方案4】:

    请记住,在客户端根本没有任何东西受到保护。如果您担心路由到没有身份验证的组件,只需确保没有数据暴露并重定向,即使在您的路由器检查身份验证之后它们也到达那里。我认为@Sam 是对的。路由可能无法按预期响应更改它的异步调用,或者可能有奇怪的行为。我从未尝试过这种方式的动态路由,但总是有条件渲染内容组件。更好的方法可能是发送调用,然后在块中重定向到路由器知道要处理的 url。只是不确定路由器能很好地处理这个问题。捕获组件在加载时检查身份验证,如果未授权,则重定向回登录。抱歉,我在这里帮不上什么忙,但条件路由几乎看起来像是一种反模式,但我想如果我们知道路由器在更改后如何呈现其数据或者它实际上是否确实如此(它们本身的路由),它可能会起作用。所以如果他们要为网址添加书签并尝试返回那将是 404 对吗?也许像 401 未经授权的显示和重定向或登录链接可能会更好?

    【讨论】:

      猜你喜欢
      • 2020-10-26
      • 2021-11-03
      • 2022-11-27
      • 2021-03-18
      • 1970-01-01
      • 2018-03-05
      • 2018-08-11
      • 2021-12-20
      • 1970-01-01
      相关资源
      最近更新 更多