简体   繁体   English

Cookie 未在 NodeJS 和 NextJS 的请求中设置

[英]Cookie not set in request with NodeJS and NextJS

I'm developing a fullstack app with Node + Express backend and NextJS front end (separate servers) and am having trouble requesting the browser to attach the cookie vended down as part of the response header from the node server.我正在开发一个带有 Node + Express 后端和 NextJS 前端(单独的服务器)的全栈应用程序,并且无法请求浏览器附加作为来自节点服务器的响应 header 的一部分出售的 cookie。 Here's the setup:这是设置:

  1. Node server is running on localhost:3000 and NextJs server is running on localhost:3001.节点服务器在 localhost:3000 上运行,NextJs 服务器在 localhost:3001 上运行。
  2. I have set up alias in etc/hosts to route someAlias.com to 127.0.0.1.我在 etc/hosts 中设置了别名,以将 someAlias.com 路由到 127.0.0.1。

Using the front end UI (port 3001) I was able to vend the cookie with JsHttp's cookie module with the following code from the backend (port 3000):使用前端用户界面(端口 3001),我能够使用 JsHttp 的 cookie 模块从后端(端口 3000)使用以下代码出售 cookie:

import { serialize } from 'cookie';
...
const cookie = serialize(TOKEN_NAME, TOKEN_VAL, {
  httpOnly: true,
  sameSite: 'none',
});

I was able to observe the Set-Cookie header in the response.我能够在响应中观察到Set-Cookie header。

However, in the subsequent requests, I did not see the cookie being attached.但是,在随后的请求中,我没有看到附加的 cookie。 I have tried fiddling with the above cookie serialization params with no success:我试过摆弄上面的 cookie 序列化参数但没有成功:

Here are the arguments I've tried:这是我试过的 arguments:

  • domain: ['.someAlias.com:3000', '.someAlias.com:3001']
  • path: '/'
  • domain: '.someAlias.com'

I have a feeling it might just be due to front end and back end server ports being different, but all requests have been initiated on the client side going to localhost:3000 (backend port).我有一种感觉,这可能只是由于前端和后端服务器端口不同,但所有请求都已在客户端发起,转到 localhost:3000(后端端口)。 So not sure what I've possibly done wrong here.所以不确定我在这里可能做错了什么。

====== UPDATE ======= ====== 更新 =======

I've run a couple more experiments, and found out that when I'm accessing a URL directly, NextJs renders the page server-side.我又进行了几次实验,发现当我直接访问 URL 时,NextJs 会在服务器端呈现页面。 When I'm transitioning between pages within the app, the page is rendered client-side where it queries the backend port 3000 directly.当我在应用程序内的页面之间转换时,页面呈现在客户端,它直接查询后端端口 3000。 Unfortunately in neither scenario did I see any cookie being set...不幸的是,在这两种情况下,我都没有看到任何 cookie 被设置...

You should set serialized cookie with res.set Express method.您应该使用res.set Express 方法设置序列化 cookie。

Alternatively, you can use res.cookie method without additional cookie package like this:或者,您可以使用res.cookie方法而无需额外的cookie package,如下所示:

res.cookie(TOKEN_NAME, TOKEN_VAL, {
  httpOnly: true,
  sameSite: 'none',
});

Note, you shouldn't worry about different ports on the same domain, since cookies are not isolated by port but domain only.请注意,您不必担心同一域上的不同端口,因为 cookies 不是按端口隔离的,而是仅按域隔离的。 No matter what port you use, cookies should be visible.无论您使用什么端口,cookies 都应该是可见的。

Since you said, "I was able to observe the Set-Cookie header in the response", I believe your node.js setting correct.既然您说“我能够在响应中观察到 Set-Cookie header”,我相信您的 node.js 设置正确。

When you get response from node js, you need to set cookies, which can be done with a npm packages easily.当您从节点 js 获得响应时,您需要设置 cookies,这可以通过 npm 包轻松完成。 I will demonstrate with js-cookie :我将使用js-cookie进行演示:

 import Cookies from "js-cookie";

you write a reusable function to set the cookies:你写一个可重用的 function 来设置 cookies:

setSession(authResult) {
    //converting everything to miliseconds
    const expiresAt =
      JSON.stringify(authResult.expiresIn * 1000) + new Date().getTime();
    // I just put  properties. I dont know how project sets
    Cookies.set("user", authResult.idTokenPayload);
    Cookies.set("jwt", authResult.idToken);
    Cookies.set("expiresAt", expiresAt);
  }

Everytime you make request you have to set headers.每次您提出请求时,您都必须设置标题。 You have to retrieve cookies based on if you are on browser or on server.您必须根据您是在浏览器上还是在服务器上来检索 cookies。 So you have to write a function if you are on server.所以如果你在服务器上,你必须写一个 function 。 Since I demonstrated how to set cookies with js-cookies , you can get the cookies easily on the browser.由于我演示了如何使用js-cookies设置 cookies ,您可以在浏览器上轻松获取 cookies 。 This reusable function to retrieve the cookie if you are on the server:如果您在服务器上,这个可重用的 function 来检索 cookie:

// cookieKey: I set three "user", "jwt","expiresAt"
export const getCookieFromReq = (req, cookieKey) => {
  console.log("req.headers", req.headers);
  // cookies are attached to the req.header.cookie. 
  const cookie = req.headers.cookie
    .split(";")
    .find((c) => c.trim().startsWith(`${cookieKey}=`));

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

Now you have to write a function to set the headers:现在你必须写一个 function 来设置标题:

import Cookies from "js-cookie";
import { getCookieFromReq } from "./directoryOf";

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

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

Now when you make request, you have to use this setAuthHeader .现在,当您提出请求时,您必须使用此setAuthHeader For example:例如:

 await axiosInstance
    .post("/blogs", blogData, setAuthHeader())
    .then((response) => response.data)
    .catch((error) => rejectPromise(error));

SAMESITE NONE

If you use SameSite=none you also need to use Secure , meaning SSL must also be used.如果您使用SameSite=none您还需要使用Secure ,这意味着还必须使用 SSL 。 You are then saying that your 2 servers are from unrelated domains, which is probably not what you want, since browsers will drop cookies aggressively.然后你说你的 2 个服务器来自不相关的域,这可能不是你想要的,因为浏览器会积极地放弃 cookies。

LOCAL PC OPTIONS本地 PC 选项

Use these settings initially on your development computer, and ensure that all URLs used in the browser and Ajax calls use http://somealias.com:3000 and http://somealias.com:3001 rather than localhost. Use these settings initially on your development computer, and ensure that all URLs used in the browser and Ajax calls use http://somealias.com:3000 and http://somealias.com:3001 rather than localhost. Cookies will then stick on a development computer.然后 Cookies 将粘贴在开发计算机上。

  • Domain=.somealias.com域=.somealias.com
  • Path=/路径=/
  • SameSite=strict SameSite=严格
  • HTTP Only仅限 HTTP

DEPLOYED OPTIONS已部署的选项

When you deploy to a proper environment, also use SSL and set the cookie option Secure .当您部署到适当的环境时,还要使用 SSL 并设置 cookie 选项Secure Most importantly, the two domains must meet hosting prerequisites of sharing the same base domain, eg:最重要的是,这两个域必须满足共享同一基本域的托管先决条件,例如:

This ensures that cookies issued are considered first party and in the same site, so that they are not dropped.这可确保发布的 cookies 被视为第一方且位于同一站点中,因此它们不会被丢弃。 If preconditions are not met, there is nothing you can do in code to fix the problem.如果不满足先决条件,则您无法在代码中执行任何操作来解决问题。

SIMILAR RESOURCE类似的资源

This Curity code example uses local development domains and same site cookie settings similar to those I've used above and may be useful to compare against.这个Curity 代码示例使用本地开发域和相同的站点 cookie 设置,类似于我在上面使用的设置,并且可能有助于进行比较。

the cookies must be sent directly from the browser to the server, which is not the case when you use nextJs. cookies 必须从浏览器直接发送到服务器,而使用nextJs时则不是这样。 because when you access to your app next js will server side render your page and then the request will be sent from nextjs server to your nodejs server so the browser will send the cookies to nextjs server not to your nodejs server.因为当您访问您的应用程序时,next js 将在服务器端呈现您的页面,然后请求将从 nextjs 服务器发送到您的 nodejs 服务器,因此浏览器会将 cookies 发送到 nextjs 服务器而不是您的 nodejs 服务器。 the solution is to send cookies manually from nextjs server to nodejs.解决方案是将 cookies 从 nextjs 服务器手动发送到 nodejs。 example with fetchApi and getServerSideProps function:以 fetchApi 和 getServerSideProps function 为例:

export async function getServerSideProps(context){ 
  try{
       const res = await fetch(`your-api-endpoint`, {
          method: 'GET',
          credentials:'include',
          headers: {
             'Access-Control-Allow-Credentials': true,
             Cookie: context.req.headers.cookie
          },
    })
    const data = await res.json()
    if(!res.ok){
       throw data 
    }
    }catch(err){
       return console.log(err)
    }

    return {
      props: {
         data
      }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM