Next.js 使用 JWT 进行身份验证

use*_*989 6 node.js express reactjs server-side-rendering next.js

我正在将一个项目从 React 转移到 Next.js 并且想知道相同的身份验证过程是否可以。基本上,用户输入他们的用户名和密码,然后通过 API (Node.js/Express) 对照数据库凭据进行检查。因此,我没有使用 Next.js 内部 api 功能,而是使用与我的 Next.js 项目完全分离的 API。

如果登录凭据正确,则会将 JWT 令牌发送回客户端。我想将其存储在本地存储中,然后重定向用户。任何未来的 http 请求都将在标头中发送令牌并通过 API 检查它是否有效。这样做可以吗?我问是因为我看到很多 Next.js auth 使用 cookie 或会话,但不知道这是否是我应该采用的“标准”方法。

Nic*_*s D 9

我的回答纯粹是基于我的经历和我读过的东西。如果我碰巧错了,请随时纠正它。

因此,我的方法是将您的令牌存储在HttpOnlycookie 中,并始终使用该 cookie 通过Authorization标头授权您对 Node API 的请求。我碰巧也在自己的项目中使用了 Node.js API,所以我知道发生了什么。

以下是我通常如何使用 Next.js 和 Node.js API 处理身份验证的示例。

为了缓解身份验证问题,我在页面中使用 Next.js 的内置getServerSideProps函数来构建一个新的可重用高阶组件来处理身份验证。在这种情况下,我将其命名为isLoggedIn

// isLoggedIn.jsx

export default (GetServerSidePropsFunction) => async (ctx) => {
  // 1. Check if there is a token in cookies. Let's assume that your JWT is stored in 'jwt'.
  const token = ctx.req.cookies?.jwt || null;

  // 2. Perform an authorized HTTP GET request to the private API to check if the user is genuine.
  const { data } = await authenticate(...); // your code here...

  // 3. If there is no user, or the user is not authenticated, then redirect to homepage.
  if (!data) {
    return {
      redirect: {
        destination: '/',
        permanent: false,
      },
    };
  }

  // 4. Return your usual 'GetServerSideProps' function.
  return await GetServerSidePropsFunction(ctx);
};
Run Code Online (Sandbox Code Playgroud)

getServerSideProps 将阻止渲染,直到函数被解析,因此请确保您的身份验证快速且不会浪费太多时间。

您可以像这样使用高阶组件。让我们称这个为一个profile.jsx,作为一个人的个人资料页面。

// profile.jsx

export default isLoggedIn(async (ctx) => {
  // In this component, do anything with the authorized user. Maybe getting his data?
  const token = ctx.req.cookies.jwt;
  const { data } = await getUserData(...); // don't forget to pass his token in 'Authorization' header.

  return {
    props: {
      data,
    },
  },
});
Run Code Online (Sandbox Code Playgroud)

这应该是安全的,因为几乎不可能操纵服务器端的任何内容,除非有人设法找到侵入您的后端的方法。

如果你想做一个POST请求,那么我通常是这样做的。

// profile.jsx

const handleEditProfile = async (e) => {
  const apiResponse = await axios.post(API_URL, data, { withCredentials: true });
  
  // do anything...
};
Run Code Online (Sandbox Code Playgroud)

在 POST 请求中,HttpOnlycookie 也会被发送到服务器,因为withCredentials参数设置为 true。

还有一种使用 Next.js 的无服务器 API 将数据发送到服务器的替代方法。您将向“代理”Next.js 的无服务器 API 发出 POST 请求,而不是向 API 发出 POST 请求,在那里它将向您的 API 执行另一个 POST 请求。


Yil*_*maz 6

没有标准方法。你应该担心安全问题。我读了这篇博文:https://hasura.io/blog/best-practices-of-using-jwt-with-graphql/

\n

这是一篇很长但很棒的博客文章。我在这里发布的所有内容都将从那里引用:

\n
\n

如果 JWT 被盗,那么窃贼可以继续使用 JWT。接受 JWT 的 API 会进行独立验证,而不依赖于 JWT 源,因此 API 服务器无法知道这是否是被盗的令牌!这就是 JWT 具有过期值的原因。并且这些\n值保持较短。通常的做法是保持在 15\n分钟左右。

\n
\n

当服务器向您发送令牌时,您必须将 JWT 持久存储在客户端上。

\n
\n

这样做会使您的应用程序容易受到 CSRF 和 XSS 攻击,\n恶意表单或脚本会使用或窃取您的令牌。我们需要将 JWT 令牌保存在某个地方,以便我们可以将其作为标头转发到我们的 API。您可能想将其保留在本地存储中;不要\xe2\x80\x99不要这样做!这很容易受到 XSS 攻击。

\n
\n

将其保存在 cookie 中怎么样?

\n
\n

在客户端创建cookie来保存JWT也容易出现XSS。如果可以在客户端上从应用程序外部的 Javascript 读取\n它 - 它可能会被盗。您可能认为 HttpOnly cookie(由服务器而不是客户端创建)会有帮助,但 cookie 很容易受到 CSRF 攻击。需要注意的是,HttpOnly\n和合理的 CORS 策略无法防止 CSRF 表单提交攻击,\n使用 cookie 需要适当的 CSRF 缓解策略。

\n

请注意,SameSite cookie 将使基于 Cookie 的方法免受 CSRF 攻击。如果您的 Auth 和 API 服务器托管在不同的域上,这可能不是一个解决方案,但\n否则它应该可以正常工作!

\n
\n

那我们把它保存在哪里呢?

\n
\n

OWASP JWT Cheatsheet 和 OWASP ASVS(应用程序安全验证标准)规定了处理和存储令牌的指南。

\n

与此相关的部分是 JWT Cheatsheet 中的客户端令牌存储和令牌侧劫持问题,以及 ASVS 的第 3 章(会话管理)和第 8 章(数据保护)。

\n
\n

从备忘单“问题:客户端的令牌存储”中:

\n
\n
    \n
  • 由浏览器自动发送(Cookie 存储)。
  • \n
  • 即使重新启动浏览器也会检索(使用浏览器 localStorage 容器)。
  • \n
  • 在出现 XSS 问题时检索(JavaScript 代码可访问 Cookie 或存储在浏览器本地/会话存储中的令牌)。
  • \n
\n
\n

“如何预防:”

\n
\n
    \n
  • 使用浏览器 sessionStorage 容器存储令牌。
  • \n
  • 调用服务时,使用 JavaScript 将其添加为 Bearer HTTP 身份验证标头。
  • \n
  • 将指纹信息添加到令牌中。
  • \n
\n

通过将令牌存储在浏览器 sessionStorage 容器中,\n令牌可能会通过 XSS 攻击被盗。但是,添加到令牌中的指纹可以防止攻击者在其计算机上重复使用被盗的令牌。要关闭攻击者的最大利用表面,请添加浏览器内容安全策略以强化执行上下文。\n

\n
\n

“指纹”

\n
\n

其中指纹是令牌劫持问题中以下准则的实现:当攻击者拦截/窃取令牌并使用目标用户身份使用它来访问系统时,就会发生此攻击。

\n
\n

「如何预防」:

\n

防止这种情况的一种方法是在令牌中添加“用户上下文”。用户上下文将由以下信息组成:

\n
\n
    \n
  • 在身份验证阶段将生成一个随机字符串。它将作为强化 cookie 发送到客户端(标志:HttpOnly +\nSecure + SameSite + cookie 前缀)。

    \n
  • \n
  • 随机字符串的 SHA256 哈希值将存储在令牌中(而不是原始值),以防止任何 XSS 问题,从而允许攻击者读取随机字符串值并设置预期的 cookie。

    \n
  • \n
\n
\n