保护React中的SPA应用程序使用登录 – 为什么这种方法不起作用?

huangapple go评论56阅读模式
英文:

Protect a SPA application in React with a login - why does this methodology not work?

问题

I understand your request. Here is the translated portion:

我正在学习ReactJS,所以我完全是新手。

我创建了一个身份验证/授权服务,在React中运行良好。我现在要做的是用登录保护主应用程序。据我了解,我需要一个高阶组件,类似于这样来保护应用程序的核心:

const withAuth = (Component) => {
  const AuthenticatedComponent = () => {
    const isAuthenticated = MyService.isUserAuthenticated();
    if (!isAuthenticated) {
      return <Navigate to="/login" />;
    }
    return <Component />;
  };

  return AuthenticatedComponent;
};

export default withAuth;

我有两个相似且很简单的组件来测试这个理论。它们基本上是这样的:

export default function DefaultLandingPage() {
  return(
    <>
    <div>
      这将是登录后的仪表板页面
    </div>
    </>
  );
}
export default function LoginPage() {
  return(
    <>
    <div>
      这是用户登录的地方
    </div>
    </>
  );
}

我尝试在我的App.js文件中使用它们。

首先我尝试了这样:

function App() {
  return (
    <>
    <Routes>
      <Route path="/" exact component={withAuth(DefaultLandingPage)} />
      <Route path="/login" element={<LoginPage />} />
    </Routes>
    </>
  );
}

这导致了以下错误:
> 在位置“/”匹配的叶子路由没有元素或组件。这意味着它将默认呈现一个空的<Outlet />,导致“空”页面。

我尝试将它更改为:

<Route path="/" exact element={withAuth(DefaultLandingPage)} />

这导致了以下错误:
> 警告:函数不是有效的React子元素。如果您从渲染中返回组件而不是<Component />,可能会发生这种情况。或者可能您是想调用此函数,而不是返回它。

有人能指点我正确的方向吗?

我找到了这篇帖子并且正在按照那个方向走;但是,我不确定我的错误逻辑在哪里。

有人能解释一下为什么我的逻辑在这里不起作用吗?

让我担忧的是,我还希望使用精细粒度的权限保护页面,并希望创建类似于这样的功能:

<Route path="/" exact element={withAuth(DefaultLandingPage)} />
<Route path="/some-sub-area" element={withAuth(withPermission(SomeSubArea, 'permission-xyz'))} />
英文:

I am learning ReactJS so I am completely new at this.

I created an authentication/authorization service that I know works in React just fine. What I am trying to do is protect the primary application with a login. As I understand, I want a high-order component such as this in order to protect the core of the application:

const withAuth = (Component) =&gt; {
  const AuthenticatedComponent = () =&gt; {
    const isAuthenticated = MyService.isUserAuthenticated();
    if (!isAuthenticated) {
      return &lt;Navigate to=&quot;/login&quot; /&gt;;
    }
    return &lt;Component /&gt;;
  };

  return AuthenticatedComponent;
};

export default withAuth;

I have two components that are pretty simple to test the theory that are both very similar. Essentially they look like this:

export default function DefaultLandingPage() {
  return(
    &lt;&gt;
    &lt;div&gt;
      This would be the dashboard page after login.
    &lt;/div&gt;
    &lt;/&gt;
  );
}
export default function LoginPage() {
  return(
    &lt;&gt;
    &lt;div&gt;
      This is where the user would log in.
    &lt;/div&gt;
    &lt;/&gt;
  );
}

I tried to use these in my App.js file.

First I tried this:

function App() {
  return (
    &lt;&gt;
    &lt;Routes&gt;
      &lt;Route path=&quot;/&quot; exact component={withAuth(DefaultLandingPage) } /&gt;
      &lt;Route path=&quot;/login&quot; element={ &lt;LoginPage /&gt; } /&gt;
    &lt;/Routes&gt;
    &lt;/&gt;
  );
}

That results in:
> Matched leaf route at location "/" does not have an element or Component. This means it will render an <Outlet /> with a null value by default resulting in an "empty" page.

I tried changing it to:

&lt;Route path=&quot;/&quot; exact element={ withAuth(DefaultLandingPage) } /&gt;

Which results in:
> Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it.

Can someone point me in the proper direction?

I did find this post and am headed down that route right now; however, I am not sure what my incorrect logic is here.

Can someone explain why my logic doesn't work here?

What concerns me, is that I want to also protect the pages with fine-grained privileges and was hoping to create something that would function like this:

&lt;Route path=&quot;/&quot; exact element={ withAuth(DefaultLandingPage) } /&gt;
&lt;Route path=&quot;/some-sub-area&quot; element={ withAuth(withPermission(SomeSubArea, &#39;permission-xyz&#39;)) } /&gt;

答案1

得分: 0

For your first case, make sure your prop component is capitalized to Component per the router documentation. For your second issue, your withAuth function returns a function reference to AuthenticatedComponent rather than treating it as a functional component. Opting to return &lt;AuthenticatedComponent/&gt; should send you in the right direction!

英文:

For your first case, make sure your prop component is capitalized to Component per the router documentation. For your second issue, your withAuth function returns a function reference to AuthenticatedComponent rather than treating it as a functional component. Opting to return &lt;AuthenticatedComponent/&gt; should send you in the right direction!

答案2

得分: 0

以下是您要翻译的内容:

The react-router-dom Route component hasn't any component, so this is the cause of the first error regarding a missing element.

The Route component's element prop takes only a React.ReactNode, e.g. it takes a JSX literal. withAuth(DefaultLandingPage) isn't valid JSX.

You've a couple options:

  1. Export a decorated version of the DefaultLandingPage component, to be rendered as JSX.

    const DefaultLandingPage = () => {
      ...
    };
    
    export default withAuth(DefaultLandingPage);
    
    function App() {
      return (
        <Routes>
          <Route path="/" element={<DefaultLandingPage />} />
          <Route path="/login" element={<LoginPage />} />
        </Routes>
      );
    }
    
  2. Implement protected routes in a more conventional method, as per the Stackoverflow answer you linked. Using Higher Order Components for authentication as a little old these days.

    const AuthenticatedComponent = () => {
      const isAuthenticated = MyService.isUserAuthenticated();
    
      return isAuthenticated
        ? <Outlet />
        : <Navigate to="/login" replace />;
    };
    
    function App() {
      return (
        <Routes>
          <Route element={<AuthenticatedComponent />}>
            <Route path="/" element={<DefaultLandingPage />} />
          </Route>
          <Route path="/login" element={<LoginPage />} />
        </Routes>
      );
    }
    

If the eventual goal is to also use role-based route protection then you'd implement something similar to the following:

const PermissionRoute = ({ permissions = [] }) => {
  const permission = /* wherever you get current user's permission */

  return !permissions.length || permissions.includes(permission) ? (
    <Outlet />
  ) : (
    <Navigate to="/login" replace /> // <-- or any safe non-permission route
  );
};
function App() {
  return (
    <Routes>
      ...
      <Route element={<PermissionRoute permissions={["...", ...]} />}>
        ...
      </Route>
      ...
    </Routes>
  );
}
英文:

The react-router-dom Route component hasn't any component, so this is the cause of the first error regarding a missing element.

The Route component's element prop takes only a React.ReactNode, e.g. it takes a JSX literal. withAuth(DefaultLandingPage) isn't valid JSX.

You've a couple options:

  1. Export a decorated version of the DefaultLandingPage component, to be rendered as JSX.

    const DefaultLandingPage = () =&gt; {
      ...
    };
    
    export default withAuth(DefaultLandingPage);
    
    function App() {
      return (
        &lt;Routes&gt;
          &lt;Route path=&quot;/&quot; element={&lt;DefaultLandingPage /&gt; } /&gt;
          &lt;Route path=&quot;/login&quot; element={&lt;LoginPage /&gt;} /&gt;
        &lt;/Routes&gt;
      );
    }
    
  2. Implement protected routes in a more conventional method, as per the Stackoverflow answer you linked. Using Higher Order Components for authentication as a little old these days.

    const AuthenticatedComponent = () =&gt; {
      const isAuthenticated = MyService.isUserAuthenticated();
    
      return isAuthenticated
        ? &lt;Outlet /&gt;
        : &lt;Navigate to=&quot;/login&quot; replace /&gt;;
    };
    
    function App() {
      return (
        &lt;Routes&gt;
          &lt;Route element={&lt;AuthenticatedComponent /&gt;}&gt;
            &lt;Route path=&quot;/&quot; element={&lt;DefaultLandingPage /&gt; } /&gt;
          &lt;/Route&gt;
          &lt;Route path=&quot;/login&quot; element={&lt;LoginPage /&gt;} /&gt;
        &lt;/Routes&gt;
      );
    }
    

If the eventual goal is to also use role-based route protection then you'd implement something similar to the following:

const PermissionRoute = ({ permissions = [] }) =&gt; {
  const permission = /* wherever you get current user&#39;s permission */

  return !permissions.length || permissions.includes(permission) ? (
    &lt;Outlet /&gt;
  ) : (
    &lt;Navigate to=&quot;/login&quot; replace /&gt; // &lt;-- or any safe non-permission route
  );
};
function App() {
  return (
    &lt;Routes&gt;
      ...
      &lt;Route element={&lt;PermissionRoute permissions={[&quot;...&quot;, ...]} /&gt;}&gt;
        ...
      &lt;/Route&gt;
      ...
    &lt;/Routes&gt;
  );
}

huangapple
  • 本文由 发表于 2023年5月7日 07:46:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/76191657.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定