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

Posted

技术标签:

【中文标题】Next.js 使用 JWT 进行身份验证【英文标题】:Next.js Authentication with JWT 【发布时间】:2021-05-29 14:47:09 【问题描述】:

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

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

【问题讨论】:

我有 Next.js/Django JWT 的生产经验,我不得不说如果你使用 JWT,你不能拥有经过身份验证的服务器端渲染页面,你必须只使受保护的路由 CSR而且您无法决定是否应在 Node.js 服务器中重定向您的用户,这将是一种条件渲染,您必须在 useEffect 钩子中决定它,因此它只在客户端而不是服务器上呈现。 您应该将令牌存储在 HttpOnly cookie 中,因为这样它会与所有请求一起传递。使用 localStorage 和 Authorization 标头,问题是当用户访问页面时您无法设置 Authorization 标头(+ 它的安全性有点低,因为在某些情况下恶意 JS 代码可能会访问它)。我经常看到这种 localStorage 模式(过去经常使用它),但这不是最好的方法。此模式适用于来自浏览器的 REST API 调用,但不适用于保护 Web 应用程序页面,cookie 更适合。 感谢您的回复。您提到了用于 REST API 调用的模式,这是我必须做的,因为 api 不在我的 next.js 应用程序中。这是一个外部 API,我必须将标头中的令牌发送到,因为 jwt 令牌是在 node.js 服务器上创建的。话虽如此,您是否建议当 jwt 令牌从服务器发送到客户端时,我将令牌存储在仅 http cookie 中,然后在进行 API 调用时,我从标头中的 cookie 发送令牌,而不是从本地存储中获取令牌? 【参考方案1】:

我的回答完全基于我的经历和阅读的内容。如果我碰巧错了,请随时纠正。

所以,我的方法是将您的令牌存储在 HttpOnly cookie 中,并始终使用该 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);
;

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,
    ,
  ,
);

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

如果你想发出 POST 请求,那么我通常会这样做。

// profile.jsx

const handleEditProfile = async (e) => 
  const apiResponse = await axios.post(API_URL, data,  withCredentials: true );
  
  // do anything...
;

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

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

【讨论】:

你有一个包含此工作示例的仓库吗? 您好!抱歉回复晚了,好久没看通知了。我确实有一个存储库,它显示了这个概念的示例用法。查看github.com/lauslim12/Asuna

以上是关于Next.js 使用 JWT 进行身份验证的主要内容,如果未能解决你的问题,请参考以下文章

在 Next.js 中创建一个 HOC(高阶组件)进行身份验证

在 Next.js 中创建一个 HOC(高阶组件)进行身份验证

在 Django 中使用 JWT 进行身份验证

使用 JWT 进行身份验证的最佳实践

使用护照 jwt 进行身份验证

使用 Airflow API 进行 JWT 身份验证