本文介绍了跨域会话Cookie(Heroku上的Express API + Netlify上的React App)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个React App调用node.js/Express中的API.

I have a React App making calls to an API in node.js/Express.

前端部署在Netlify(https)中,后端部署在Heroku(https)中.

Frontend is deployed in Netlify (https), Backend deployed on Heroku (https).

我的问题:

  • 开发环境(localhost)中的所有工作
  • 在生产环境(Netlify/Heroku)中,用于注册和登录的api调用似乎有效,但是会话cookie未存储在浏览器中.因此,对API中受保护路由的任何其他调用都会失败(因为我没有收到用户凭据).
  • Everything working in dev environment (localhost)
  • In production (Netlify/Heroku), the api calls to register and login seem to work but the session cookie is not stored in the browser. Because of that, any other calls to protected routes in the API fail (because I don't receive the user credentials).







对话很便宜,请告诉我代码....

  • I'm using passport (local strategy), express-session, cors.

App.js

require('./configs/passport');

// ...

const app = express();

// trust proxy (https://stackoverflow.com/questions/64958647/express-not-sending-cross-domain-cookies)
app.set("trust proxy", 1);

app.use(
  session({
    secret: process.env.SESSION_SECRET,
    cookie: {
      sameSite: process.env.NODE_ENV === "production" ? 'none' : 'lax',
      maxAge: 60000000,
      secure: process.env.NODE_ENV === "production",
    },
    resave: true,
    saveUninitialized: false,
    ttl: 60 * 60 * 24 * 30
  })
);

app.use(passport.initialize());
app.use(passport.session());

// ...


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

//...

app.use('/api', require('./routes/auth-routes'));
app.use('/api', require('./routes/item-routes'));


CRUD端点(例如item-routes.js):

// Create new item
router.post("/items", (req, res, next) => {
    Item.create({
        title: req.body.title,
        description: req.body.description,
        owner: req.user._id // <-- AT THIS POINT, req.user is UNDEFINED
    })
    .then(
        // ...
    );
});

前端(React App):

  • 将Axios与选项"withCredentials"一起使用设置为true ...
  • 用户注册和登录:

    class AuthService {
      constructor() {
        let service = axios.create({
          baseURL: process.env.REACT_APP_API_URL,
          withCredentials: true
        });
        this.service = service;
      }
    
      signup = (username, password) => {
        return this.service.post('/signup', {username, password})
        .then(response => response.data)
      }
    
      login = (username, password) => {
        return this.service.post('/login', {username, password})
        .then(response => response.data)
      }
    
      //...
    }
    

    正在创建一个新项目...:

        axios.post(`${process.env.REACT_APP_API_URL}/items`, {
            title: this.state.title,
            description: this.state.description,
        }, {withCredentials:true})
        .then( (res) => {
            // ...
        });
    

    推荐答案

    对自己问题的简短回答:

    它没有按预期运行,因为我在Chrome Incognito上进行了测试,并且默认情况下,第三方Cookie在隐身模式下被阻止().

    下面是一个列表,其中包含一些要检查您是否遇到类似问题的内容;)

    Below is a list with some things to check if you're having a similar issue ;)

    • (后端)添加信任代理"选项

    如果要在Heroku上进行部署,请添加以下行(您可以在会话设置之前添加它).

    If you're deploying on Heroku, add the following line (you can add it before your session settings).

    app.set("trust proxy", 1);
    
    

    • (后端)检查您的会话设置
    • 尤其要检查选项 sameSite secure (更多详细信息在这里).

      In particular, check the option sameSite and secure (more details here).

      下面的代码将在生产环境中设置 sameSite:'none' secure:true :

      The code below will set sameSite: 'none' and secure: true in production:

      app.use(
        session({
          secret: process.env.SESSION_SECRET || 'Super Secret (change it)',
          resave: true,
          saveUninitialized: false,
          cookie: {
            sameSite: process.env.NODE_ENV === "production" ? 'none' : 'lax', // must be 'none' to enable cross-site delivery
            secure: process.env.NODE_ENV === "production", // must be true if sameSite='none'
          }
        })
      );
      

      • (后端)CORS配置
      • app.use(
          cors({
            credentials: true,
            origin: [process.env.FRONTEND_APP_URL]
          })
        );
        

        • (前端)发送凭据
        • 确保在API调用中发送凭据(您需要对API进行的所有调用(包括用户登录调用)都执行此操作.

          Make sure you're sending credentials in your API calls (you need to do that for all calls you make to the API, including the call for user login).

          如果您使用的是axios,则可以使用 withCredentials 选项.例如:

          If you're using axios, you can do use withCredentials option. For example:

              axios.post(`${process.env.REACT_APP_BACKEND_API_URL}/items`, {
                  title: this.state.title,
                  description: this.state.description,
              }, {withCredentials:true})
              .then( (res) => {
                  // ...
              });
          

          • ...并处理浏览器限制...:
          • 如上所述,某些浏览器可能对第三方Cookie有所限制(例如,Chrome当前以隐身模式阻止第三方Cookie),并且您预计这些限制在未来几年将会增加.

            As stated above, some browsers might have restrictions for third party cookies (for example, Chrome currently blocks third-party cookies in Incognito mode) and you can expect those restrictions to increase in the coming years.

            请记住,以下是一些选择:

            Keeping that in mind, here are some options:

            • 实施后端&同一域下的前端

            • Implement backend & frontend under the same domain

            实施后端&同一域(例如example.com和api.example.com)的子域下的前端

            Implement backend & frontend under subdomains of the same domain (example, example.com & api.example.com)

            将后端API置于代理下(如果您使用的是netlify,则可以轻松地使用_redirects设置代理文件)

            Have your backend API under a proxy (if you're using netlify, this can easily setup a proxy using a _redirects file)

            这篇关于跨域会话Cookie(Heroku上的Express API + Netlify上的React App)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-26 02:59
查看更多