redis 为什么Express Session变量(userID)不能在浏览器中创建cookie?

enyaitl3  于 2022-10-31  发布在  Redis
关注(0)|答案(2)|浏览(141)

我用Express Session和Redis创建了一个简单的服务器。Redis服务器正在运行(当我键入“redis-cli ping”时,我收到了“PONG”),我声明了名称空间并导出了接口SessionData,以允许我在请求会话时存储userID。(使用一个基本的索引.d.ts)。但是,当我发出登录请求时,变量userID存储在req.session中,但由于cookie没有设置到浏览器,它会立即被忘记/擦除。似乎每个请求都会产生一个新的会话,cookie永远不会保存。
应用程序、Redis和会话Cookie设置:

// ...
const app = express();

const RedisStore = connectRedis(session);
const redis = new Redis();

app.use(
    session({
      name: 'testcookie',
      store: new RedisStore({
        client: redis,
        disableTouch: true,
        port: 6379,
        host: 'localhost',
      }),
      cookie: {
        maxAge: 36000000 * 24 * 365,
        httpOnly: true,
        sameSite: 'lax',
        secure: false,
      },
      saveUninitialized: false,
      secret: 'secret',
      resave: false,
    })
  );
// ...

登录突变:

@Mutation(() => UserResponse)
  async login(
    @Arg("usernameOrEmail") usernameOrEmail: string,
    @Arg("password") password: string,
    @Ctx() { req }: MyContext
  ): Promise<UserResponse> {
    // gets user via inputs (everything works here)
    // ...

    req.session.userID = user.id;
    // userID is set to be a number, as is user.id
    // logging req.session.userID works perfectly if done right here

    return { user };
  }

查询以检查是否已登录:

@Query(() => User, { nullable: true })
  async me(@Ctx() { req }: MyContext): Promise<User | undefined> {
    // logging req.session.userID shows undefined

    return (req.session.userID)
      ? await User.findOne({ id: req.session.userID })
      : undefined;
  }

更新(解决方案):通过进入GraphQL的设置并将“request.credentials”属性更改为“include”,解决了此问题。

8yoxcaq7

8yoxcaq71#

我正在关注2022年6月发布的Fullstack React GraphQL TypeScript Tutorial。自2021年以来,Apollo的GraphQL Playground已不复存在-取而代之的是Apollo studio沙箱(https://studio.apollographql.com/sandbox/explorer
在新的阿波罗工作室中,我找不到将request.credentials设置为include的方法。
遵循这些线索后:
https://community.apollographql.com/t/allow-cookies-to-be-sent-alongside-request/920

https://github.com/apollographql/apollo-server/issues/5775
我找到了一个不太好的解决方案,但它符合我的需要。
基本上,在我的设置中,它看起来像会话。cookie参数'sameSite'和'secure'需要不同的值,这取决于你是否希望你的前端添加cookie和apollo studio添加cookie。这不是理想的-但我找不到任何其他参数组合,为这两个工作。到目前为止,我只找到互斥的设置。
在我的服务器的索引.ts上
当我想从前端localhost:3000设置cookie时,我使用此会话设置

app.use(
    session({
      name: COOKIE_NAME,
      store: new RedisStore({ client: redis, disableTouch: true }),
      saveUninitialized: false,
      cookie: {
        maxAge: 1000 * 60 * 60 * 24 * 12, //2 weeks
        httpOnly: true,
        sameSite: "lax", // sets cookie from frontend localhost:3000
        secure: false, // sets cookie from frontend localhost:3000
      },
      secret: "shhhhdonttell",
      resave: false,
    })
  );

如果我想从apollostudio设置cookie和会话userId,我实际上会更改会话设置
当我想从后端localhost:4000/graphql设置cookie时,我使用这个会话设置

app.use(
    session({
      name: COOKIE_NAME,
      store: new RedisStore({ client: redis, disableTouch: true }),
      saveUninitialized: false,
      cookie: {
        maxAge: 1000 * 60 * 60 * 24 * 12, //2 weeks
        httpOnly: true,
        sameSite: "none", // sets cookie from apollo studio
        secure: true, // sets cookie from apollo studio
      },
      secret: "shhhhdonttell",
      resave: false,
    })
  );

请评论,如果你知道一种方法,使用相同的设置,以允许来自前端和后端的cookie。

对于那些正在学习教程并希望了解更多详细信息的人,这里是设置的其他重要部分。

这是我的整个index.ts文件从后端。注意-我是8.5小时的教程-所以不要担心,如果你不认识一些部分。

import "reflect-metadata";
import { COOKIE_NAME, __prod__ } from "./constants";
import express from "express";
import { ApolloServer } from "apollo-server-express";
import { buildSchema } from "type-graphql";
import { HelloResolver } from "./resolvers/hello";
import { PostResolver } from "./resolvers/post";
import { UserResolver } from "./resolvers/user";
import ioredis from "ioredis";
import session from "express-session";
import connectRedis from "connect-redis";
import { MyContext } from "./types";
import cors from "cors";
import { getDataSource } from "./data-source";
import { Post } from "./entities/Post";

const PORT = 4000;

const main = async () => {
  const dbconn = await getDataSource()

  if (typeof dbconn === "boolean") return

  console.log('starting migrations')
  dbconn.runMigrations()
  // await Post.delete({})

  // const orm = await MikroORM.init(microConfig);
  // orm.getMigrator().up();
  console.log('migrations finished')

  const app = express();

  const RedisStore = connectRedis(session);
  const redis = new ioredis();
  // const redisClient = new redis({ legacyMode: true });
  redis.connect().catch((err) => `RedisClient Connect error: ${err}`);

  !__prod__ && app.set("trust proxy", 1);

  app.use(
    cors({
      origin: ["http://localhost:3000", "http://localhost:4000/graphql", "https://studio.apollographql.com",],
      credentials: true,
    })
  );

  app.use(
    session({
      name: COOKIE_NAME,
      store: new RedisStore({ client: redis, disableTouch: true }),
      saveUninitialized: false,
      cookie: {
        maxAge: 1000 * 60 * 60 * 24 * 12, //2 weeks
        httpOnly: true,
        sameSite: "lax", 
        secure: __prod__,
      },
      secret: "shhhhdonttell",
      resave: false,
    })
  );

  // app.use(
  //   session({
  //     name: COOKIE_NAME,
  //     store: new RedisStore({ client: redis, disableTouch: true }),
  //     saveUninitialized: false,
  //     cookie: {
  //       maxAge: 1000 * 60 * 60 * 24 * 12, //2 weeks
  //       httpOnly: true,
  //       // sameSite: "lax", // front end 
  //       // secure: __prod__, //true in production, is can be false for frontend
  //       sameSite: "none", //csrf //must be nne for apollo sandbox
  //       secure: true, //true in production, false on localhost // must be true for apollo sandbox even in dev
  //     },
  //     secret: "shhhhdonttell",
  //     resave: false,
  //   })
  // );

  const apolloServer = new ApolloServer({
    schema: await buildSchema({
      resolvers: [HelloResolver, PostResolver, UserResolver],
      validate: false,
    }),
    context: ({ req, res }): MyContext => ({
      dbconn,
      req,
      res,
      redis,
    }),
  });

  await apolloServer.start();
  apolloServer.applyMiddleware({
    app,
    cors: false,
    // cors: {
    //   // origin: "http://localhost:3000",
    //   origin: [
    //     "http://localhost:3000",
    //     "https://studio.apollographql.com",
    //     "http://localhost:4000/graphql",
    //   ],
    //   credentials: true,
    // },
  });

  app.listen(PORT, () => {
    console.log(`server started on localhost:${PORT}`);
  });
};

main().catch((err) => {
  console.error(err);
});

这是我的数据源(新的方式使typeORM连接!)

import { Post } from "./entities/Post";
import { Users } from "./entities/Users";
import { DataSource } from "typeorm";
import path from "path"
import "reflect-metadata";
import { Upvote } from "./entities/Upvote";

export async function getDataSource() {

    const typeormDataSource = new DataSource({
        type: "postgres",
        host: "localhost",
        port: 5432,
        username: "nicole",
        password: "yourpassword",
        database: "bendb2", //your dbname
        logging: false,
        synchronize: true,
        entities: [Post, Users, Upvote],//take out entites you have't made yet.
        subscribers: [],
        migrations: [path.join(__dirname, "./migrations/*")],

    })
    let datasource = await typeormDataSource.initialize().catch(err => {
        console.error(err)
        console.log("Database connection NOT initialized")
        return false
    })
    return datasource
}

在createUrqlClient.tsx前端文件中,我添加了

export const createUrqlClient = (ssrExchange: any) => ({
  url: "http://localhost:4000/graphql",
  fetchOptions: {
    credentials: "include" as const, 
  },
  exchanges: [...]

这里是apollo studio中所需设置的快照。要打开这些设置,请点击SANDBOX输入中左上角的设置/齿轮图标。

请确保将“x-forwarded-proto”“https”添加到共享头中。

7vux5j2d

7vux5j2d2#

答案形式@NicoWheat是部分正确的(我猜,纠正我,如果我错了).它工作时,我发送请求与apollo工作室(与sameSite:“无”和安全:true),但无论选项是什么,当我通过localhost:3000进行变异时,cookie仍然没有为我设置。
编辑:我错了,在frontend目录中按照create urql client中的选项操作后,它对我起作用了,非常感谢和欣赏。

相关问题