本文介绍了如何在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 tutorial/articles, but not quite understand it. Here are what I have tried.

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

  1. When 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, that 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? 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 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中的存在令牌,然后如果令牌被吊销或列入黑名单怎么办,他们如何处理它,因为他们没有将令牌发送到服务器?还是在每次客户端页面更改时都必须将令牌发送到服务器?

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 changing?

推荐答案

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

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

Next.js使用App组件初始化页面. _app页面负责呈现我们的页面.我们在_app.js上对用户进行身份验证,因为我们从getInitialProps返回的任何内容都可以被所有其他页面访问.我们在这里对用户进行身份验证,身份验证决定将传递到页面,从页面到页眉,因此每个页面都可以确定用户是否已通过身份验证. (请注意,可以使用redux来完成,而无需进行支撑钻探,但这会使答案变得更加复杂)

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中实现身份验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-30 05:39