如何在 Next.js 中实现身份验证 [英] How to implement authentication in Next.js

查看:29
本文介绍了如何在 Next.js 中实现身份验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 Next.js 的新手,我正在努力使用 jwt 令牌的身份验证系统.我想知道存储 jwt 令牌和使用身份验证系统路由的最佳/标准方式是什么.我一直在尝试不同的方法,来自不同的教程/文章,但不太了解.这是我尝试过的.

I am new to Next.js and I am struggling with the authentication system using jwt token. I want to know what is the best / standard way to store the jwt token and routing with the authentication system. I have been trying different approaches, from different tutorials/articles, but do not quite understand it. Here are what I have tried.

  1. 当用户登录时,它会将用户名/密码发送到一个分离的 API 服务器(例如处理后端内容的新项目),服务器将响应 access-token,然后在 Next.js 项目中,我使用接收到的令牌设置 cookie.在 Next.js 项目中,受保护的路由将使用 withAuth hoc 包装,它将检查 cookie 中的令牌.这种方法的问题在于它容易受到 XSS 攻击,因为 cookie 没有 httpOnly 标志.

  1. When the user login, it sends username/password to a separated API server (ex. new project that handles backend stuff), the server will respond with the access-token, then in Next.js project, I set the cookie with that received token. In Next.js project, protected routes will be wrapped with a withAuth hoc, which will check for the token in a cookie. The problem with this approach is that it is vulnerable to XSS because the cookie has no httpOnly flag.

这和 1.) 类似.但是使用 localStorage,问题是 access-token 在第一次请求时无法发送到服务器.(这个我不确定,但据我了解,在每个 HTTP 请求中,我必须手动粘贴我的 access-token ,那么我无法控制的请求呢?例如第一个请求或使用 <a> 标签).

This is similar to 1.) but using localStorage, the problem is access-token could not be sent to the server on the first request. (This one I'm not sure, but in my understanding, in every HTTP request, I must stick my access-token manually, so what about requests that I have no control over? ex. first request or using <a> tag).

我在 Next.js 服务器(自定义快速服务器)中编写了身份验证后端.当用户登录时,服务器会对其进行验证,然后设置一个 httpOnly cookie.然后问题是,使用客户端路由(使用 Next.js 路由器转到 URL),它无法检查令牌.例如,如果一个页面使用 withAuth hoc 包装,但它无法使用 javascript 访问 cookie 中的令牌.

I wrote authentication backend inside Next.js server (custom express server). When the user login, the server will validate it and then set an httpOnly cookie. Then the problem is, with client-side routing (go to URL using Next.js Router), it could not check for token. For example, if a page is wrapped with withAuth hoc, but it cannot access the token inside cookies with javascript.

而且我见过很多人,在受保护路由的getInitialProps中,他们只检查cookie/localStorage中是否存在token,那么如果token被撤销或列入黑名单怎么办?他们处理它是因为他们没有将令牌发送到服务器吗?还是我必须在每次客户端页面更改时将令牌发送到服务器?

And I've seen a lot of people, in getInitialProps of the protected route, they only check for existence token in cookie / localStorage, then what if the token is being revoked or blacklisted, how do they handle it because they did not send the token to the server? Or do I have to send the token to the server in every client-side page change?

推荐答案

由于我们正在隔离,我有足够的时间来回答这个问题.这将是一个很长的答案.

Since we are on quarantine I have enough time to answer this question. It will be a long answer.

Next.js 使用 App 组件来初始化页面._app page 负责渲染我们的页面.我们在 _app.js 上对用户进行身份验证,因为我们从 getInitialProps 返回的任何内容都可以被所有其他页面访问.我们在这里对用户进行身份验证,身份验证决策将传递到页面,从页面到标题,因此每个页面都可以决定用户是否通过身份验证.(请注意,它可以使用 redux 来完成而不用 prop 钻孔,但它会使答案更复杂)

Next.js uses the App component to initialize the pages. _app page is responsible for rendering our pages. We authenticate users on _app.js because anything that we return from getInitialProps can be accessed by all of the other pages. We authenticate user here, authentication decision will be passed to pages, from pages to header, so each page can decide if the user is authenticated or not. (Note that it could be done with redux without prop drilling but it would make the answer more complex)

  static async getInitialProps({ Component, router, ctx }) {
    let pageProps = {};
    const user = process.browser
      ? await auth0.clientAuth()
      : await auth0.serverAuth(ctx.req); // I explain down below

    //this will be sent to all the components
    const auth = { user, isAuthenticated: !!user };
    if (Component.getInitialProps) {
      pageProps = await Component.getInitialProps(ctx);
    }

    return { pageProps, auth };
  }

  render() {
    const { Component, pageProps, auth } = this.props;
    return <Component {...pageProps} auth={auth} />;
  }
}

如果我们在浏览器上并且需要检查用户是否经过身份验证,我们只需从浏览器中检索 cookie,这很容易.但是我们总是要验证令牌.它与浏览器和服务器使用的过程相同.我将在下面解释.但是如果我们在服务器上.我们无法访问浏览器中的 cookie.但是我们可以从req"中读取.对象,因为 cookie 附加到 req.header.cookie. 这是我们访问服务器上 cookie 的方式.

If we are on the browser and need to check if a user is authenticated, we just retrieve the cookie from the browser, which is easy. But we always have to verify the token. It is the same process used by browser and server. I will explain down below. But if we are on the server. we have no access to the cookies in the browser. But we can read from the "req" object because cookies are attached to the req.header.cookie. this is how we access to cookies on the server.

async serverAuth(req) {
    // console.log(req.headers.cookie) to check
    if (req.headers.cookie) {
      const token = getCookieFromReq(req, "jwt");
      const verifiedToken = await this.verifyToken(token);
      return verifiedToken;
    }
    return undefined;
  }

这里是 getCookieFromReq().请记住,我们必须考虑功能性.

here is getCookieFromReq(). remember we have to think functional.

const getCookieFromReq = (req, cookieKey) => {
  const cookie = req.headers.cookie
    .split(";")
    .find((c) => c.trim().startsWith(`${cookieKey}=`));

  if (!cookie) return undefined;
  return cookie.split("=")[1];
};

一旦我们得到cookie,我们必须对其进行解码,提取过期时间以查看它是否有效.这部分很简单.我们必须检查的另一件事是 jwt 的签名是否有效.对称或非对称算法用于签署 jwt.您必须使用私钥来验证对称算法的签名.RS256 是 API 的默认非对称算法.使用 RS256 的服务器为您提供了一个链接,以获取 jwt 以使用密钥来验证签名.你可以使用 [jwks-rsa][1] 也可以自己做.您必须创建一个证书,然后验证令牌是否有效.

Once we get the cookie, we have to decode it, extract the expiration time to see if it is valid or not. this part is easy. Another thing we have to check is if the signature of the jwt is valid. Symmetric or asymmetric algorithms are used to sign the jwt. You have to use private keys to validate the signature of symmetric algorithms. RS256 is the default asymmetric algorithms for APIs. Servers that use RS256, provide you with a link to get jwt to use the keys to validate the signature. You can either use [jwks-rsa][1] or you can do on your own. You have to create a certificate and then verify if the token is valid.

假设我们的用户现在通过了身份验证.你说,而且我见过很多人,在受保护路由的 getInitialProps 中,他们只检查 cookie/localStorage 中的存在令牌,".我们使用受保护的路由仅向授权用户提供访问权限.为了访问这些路由,用户必须显示他们的 jwt 令牌,并且 express.js 使用中间件来检查用户的令牌是否有效.由于您已经看过很多示例,因此我将跳过这一部分.

Assume that our user authenticated now. You said, "And I've seen a lot of people, in getInitialProps of the protected route, they only check for existence token in cookie / localStorage,". We use protected routes to give access only to the authorized users. In order to access those routes, users have to show their jwt tokens and express.js uses middlewares to check if the user's token is valid. Since you have seen a lot of examples, I will skip this part.

那么如果令牌被撤销或列入黑名单怎么办,因为他们没有将令牌发送到服务器,他们如何处理呢?还是我必须在每个客户端页面更改时将令牌发送到服务器?"

"then what if the token is being revoked or blacklisted, how do they handle it because they did not send the token to the server? Or do I have to send the token to a server in every client-side page changing?"

通过验证令牌过程,我们 100% 确定令牌是否有效.当客户端要求服务器访问一些秘密数据时,客户端必须将令牌发送到服务器.想象一下,当您挂载组件时,组件要求服务器从受保护的路由中获取一些数据.服务器将提取 req 对象,获取 jwt 并使用它从受保护的路由中获取数据.浏览器和服务器获取数据的实现方式不同.如果浏览器发出请求,它只需要相对路径,但服务器需要绝对路径.如您所知,获取数据是通过组件的 getInitialProps() 完成的,并且该函数在客户端和服务器上都执行.这里是你应该如何实现它.我刚刚附上了 getInitialProps() 部分.

with verifying token process we are 100% sure if the token is valid or not. When a client asks the server to access some secret data, the client has to send the token to the server. Imagine when you mount the component, component asks the server to get some data from the protected routes. The server will extract the req object, take the jwt and use it to fetch data from the protected routes. Implementation of the fetching data for browser and server are different. And if the browser makes a request, it just needs the relative path but the server needs an absolute path. As you should know fetching data is done getInitialProps() of the component and this function executed on both client and server. here is how you should implement it. I just attached the getInitialProps() part.

MyComponent.getInitialProps = async (ctx) => {
  const another = await getSecretData(ctx.req);
 //reuslt of fetching data is passed to component as props
  return { superValue: another };
};



    const getCookieFromReq = (req, cookieKey) => {
      const cookie = req.headers.cookie
        .split(";")
        .find((c) => c.trim().startsWith(`${cookieKey}=`));

      if (!cookie) return undefined;
      return cookie.split("=")[1];
    };

   
    const setAuthHeader = (req) => {
      const token = req ? getCookieFromReq(req, "jwt") : Cookies.getJSON("jwt");

      if (token) {
        return {
          headers: { authorization: `Bearer ${token}` },
        };
      }
      return undefined;
    };

    
    export const getSecretData = async (req) => {
      const url = req ? "http://localhost:3000/api/v1/secret" : "/api/v1/secret";
      return await axios.get(url, setAuthHeader(req)).then((res) => res.data);
    };



  [1]: https://www.npmjs.com/package/jwks-rsa

这篇关于如何在 Next.js 中实现身份验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆