React Web应用程序中的安全身份验证结构



我目前正在学习 React 并完成了一些课程,但仍然没有完全了解如何为安全的 Web 应用程序创建适当的结构。

对于登录,注册流程我使用 Firebase SDK。登录后,用户将被重定向到专用路由。现在我只有 2 个用户角色。来宾和登录用户。这使我能够使用内置的 firebase 函数创建私有路由。这是第一个问题,因为一旦我添加不同的角色,它就无法扩展,因为它会迫使我向后端发送请求以检查用户是什么角色,从而检查他可以访问哪些页面。

if (firebase.auth().currentUser === null) {
console.log("not logged in")
return     (<Redirect
to={{
pathname: "/signin",
state: {
from: props.location
}
}}
/>);
}

所以我认为最简单的选择是使用上下文,它确实有效。用户登录后,服务器将发送一个用户对象,应用程序将在会话的其余部分引用该对象。我遵循了一堆教程,它们都有相同的问题,即当使用具有反应功能的 chrome 开发人员工具时,您可以编辑用户的状态并绕过私有路由等。

第二次尝试:

<UserContext.Consumer>{(context)=>{
const {isLoggedIn} = context
return(
<Route
{...rest}
render={props => {
if (isLoggedIn) {
console.log("not logged in")
return     (<Redirect
to={{
pathname: "/signin",
state: {
from: props.location
}
}}
/>);

如果有人能指出我一个方向,我将不胜感激,因为我似乎错过了一些重要的东西。

编辑1:或者仅仅是一旦你构建了应用程序,你就不能再访问这些状态,它被认为是安全的?

当使用带有 React 功能的 Chrome 开发者工具时,您可以编辑用户的状态并绕过私有路由

您的路线永远不会真正私密。它们是浏览器下载和呈现的 JavaScript 捆绑包的一部分,因此它们永远不应该包含任何秘密内容。如果他们真的愿意,任何人都可以阅读此代码。

考虑一下:

if (loggedIn) {
return <div>Secret data: ABC</div>;
}

字符串"ABC"包含在您的应用程序构建中,不再是真正的秘密。普通用户不知道如何获取它,但开发人员可能会,例如通过在开发人员控制台中切换某些状态。

但是,来自 Firestore(或任何其他后端服务(的数据应得到适当的保护。在将此数据发送到浏览器之前,会在服务器端完成权限检查。因此,除非用户具有所需的权限,否则即使有人在开发人员控制台中篡改了您的客户端代码,数据也永远不会暴露给错误的人。

if (loggedIn) {
fetchDataFromBackend();
}

如果有人将loggedIn更改为true以便调用fetchDataFromBackend()并不重要;服务器将确保除非用户具有适当的权限(例如已登录(,否则不会返回数据。对于 Firebase (Firestore(,此保护是通过安全规则实现的。

顺便说一下,使用Firebase获取当前用户的推荐方法是向Auth对象添加侦听器:

firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
});

您可以将其放在顶级组件中,并通过上下文与子组件共享user对象。这样你就不必到处打电话给firebase.auth()。如果您需要一些灵感,这是一个很好的起点:https://usehooks.com/useAuth/

我认为您在前端站点上所做的很好,但是您还需要后端的逻辑来保护您的路由。这意味着用户可能能够通过前端的开发工具绕过您的路由保护,但您的后端只会向他发送错误消息,因为它会识别出他没有允许。

你可以使用像这样的高阶函数来做到这一点:

const authenticationWrapper = createResolver(
async ( models, session, SALT ) => {
try {
if (!session) {
throw new Error("No valid credentials!");
}
const { id } = verify(session.token, salt);
const valid = databasecall //
if (!valid) {
throw new Error("No valid user!");
}
return true
} catch (error) {
session.destroy((err) => {
if (err) {
console.error(err);
}
});
}
}
);

所有私有后端函数都将被包装进去,并且每次都会检查用户的身份验证。

签入前端和后端的原则称为双重身份验证。您可以在此处阅读有关它的更多信息。

最新更新