如果客户端在不同的域中,则快速会话不会持续存在

Express session doesn't persist if the client is on a different domain

tl:dr;

A Node (express) 服务器托管在 Heroku 上,UI 托管在 Netlify 上。当 UI 对服务器进行 REST API 调用时,会话不会持续存在(但如果我 运行 在本地,它会持续存在。localhost:5000 在服务器上,localhost:3000 on UI。UI 正在代理 package.json) 的请求。

代码片段

session.ts

export const sessionConfig = {
  secret: process.env.SESSION_KEY,
  store: new RedisStore({ client: redisClient }),
  resave: true,
  saveUninitialized: true,
  cookie: {
    secure: process.env.NODE_ENV === 'production',
    sameSite: process.env.NODE_ENV === "production" ? 'none' : 'lax',
  },
};

server.ts

const app = express();

app.use(express.json());
app.use(cookieParser());

app.set('trust proxy', 1);
app.use(session(sessionConfig)); // This sessionConfig comes from the file above

app.use(cors({
  credentials: true,
  origin: process.env.CLIENT_URL,
}));

我用谷歌搜索了类似 express session not persist when cross domain request 的内容。然后,我看到了像 and this 这样的帖子。似乎 app.set('trust proxy', 1) 将确保为跨域请求保留会话数据。显然,就我而言,仍然缺少一些东西。

有人看到我做错了什么吗?任何建议将不胜感激!

PS:

我正在使用会话进行验证码测试,看起来像...

captch.ts

CaptchaRouter.get('/api/captcha', async (req: Request, res: Response) => {
  const captcha = CaptchaService.createCaptcha();
  req.session.captchaText = captcha.text;
  res.send(captcha.data);
});

CaptchaRouter.post('/api/captcha', async (req: Request, res: Response) => {
    if (req.session.captchaText !== req.body.captchaText) {
      throw new BadRequestError('Wrong code was provided');
    }

    // The client sent the correct captcha
  },
);

另一个PS: 以下是响应 heders 的样子:

Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://example.netlify.app
Connection: keep-alive
Content-Length: 46
Content-Type: application/json; charset=utf-8
Date: Sun, 09 Jan 2022 00:00:00 GMT
Etag: W/"2e-cds5jiaerjikllkslaxmalmird"
Server: Cowboy
Set-Cookie: connect.sid=s%3ramdon-string-here; Path=/; Expires=Sun, 09 Jan 2022 00:00:00 GMT; HttpOnly; Secure; SameSite=None
Vary: Origin
Via: 1.1 vegur
X-Powered-By: Express

原因是客户端(托管在 Netlify 上)没有代理 API 请求。

解决方案是:

  1. 在客户端public下添加_redirects
/api/*  https://server.herokuapp.com/api/:splat  200

/*  /index.html  200

  1. 确保来自客户端的 API 请求将以根 URL
  2. 开头
return axios({ method: 'POST', url: '/api/example', headers: defaultHeaders });

为了将来参考,这是我的会话配置

const sessionConfig = {
  secret: process.env.SESSION_KEY || 'This fallback string is necessary for Typescript',
  store: new RedisStore({ client: redisClient }),
  resave: false,
  saveUninitialized: true,
  cookie: {
    secure: process.env.NODE_ENV === 'production', // Prod is supposed to use https
    sameSite: process.env.NODE_ENV === "production" ? 'none' : 'lax', // must be 'none' to enable cross-site delivery
    httpOnly: true,
    maxAge: 1000 * 60
  } as { secure: boolean },
};

...这里是 server.ts

const app = express();
const port = process.env.PORT || 5000;

app.use(express.json());

app.set('trust proxy', 1);
app.use(session(sessionConfig));

app.use(cors({
  credentials: true,
  origin: process.env.CLIENT_URL,
}));

(正如@Matt Davis 指出的那样,cookieParser 是不必要的)

PS

我没有尝试在会话配置中设置 cookie.domain。如果这是设置给客户端 URL(由 Netlify 提供),会话 cookie 是否持续存在?