英文:
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) => {
const AuthenticatedComponent = () => {
const isAuthenticated = MyService.isUserAuthenticated();
if (!isAuthenticated) {
return <Navigate to="/login" />;
}
return <Component />;
};
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(
<>
<div>
This would be the dashboard page after login.
</div>
</>
);
}
export default function LoginPage() {
return(
<>
<div>
This is where the user would log in.
</div>
</>
);
}
I tried to use these in my App.js
file.
First I tried this:
function App() {
return (
<>
<Routes>
<Route path="/" exact component={withAuth(DefaultLandingPage) } />
<Route path="/login" element={ <LoginPage /> } />
</Routes>
</>
);
}
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:
<Route path="/" exact element={ withAuth(DefaultLandingPage) } />
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:
<Route path="/" exact element={ withAuth(DefaultLandingPage) } />
<Route path="/some-sub-area" element={ withAuth(withPermission(SomeSubArea, 'permission-xyz')) } />
答案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 <AuthenticatedComponent/>
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 <AuthenticatedComponent/>
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:
-
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> ); }
-
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:
-
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> ); }
-
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>
);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论