【问题标题】:How to set cookies with FastAPI for cross-origin requests如何使用 FastAPI 为跨域请求设置 cookie
【发布时间】:2021-01-16 05:03:45
【问题描述】:

我有一个基于应用程序的 FastAPI,它用作网站的后端,目前部署在具有外部 IP 的服务器上。前端位于另一个开发人员处,暂时在本地托管中。 工作之初遇到了一个CORS问题,使用我在网上找到的如下代码解决了:

from fastapi.middleware.cors import CORSMiddleware
...
app.add_middleware(
    CORSMiddleware,
    allow_origins=['http://localhost:3000'],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

添加允许前端正确发出请求,但出于某种原因,设置为发送(并在 Swagger UI 中正常工作)的 cookie 未在前端设置。 客户端看起来像:

axios({
            method: 'POST',
            baseURL: 'http://urlbase.com:8000',
            url: '/login',
            params: {
                mail: 'zzz@zzz.com',
                password: 'xxxxxx'
            },
            withCredentials: true
        }).then( res => console.log(res.data) )
        .catch( err => console.log(err))

【问题讨论】:

  • 你有办法解决这个问题吗?

标签: javascript python cookies axios fastapi


【解决方案1】:

在 FastAPI 中设置和读取 cookie 可以通过使用 Request 类来完成:

设置 cookie refresh_token

from fastapi import Response

@app.get('/set')
async def setting(response: Response):
    response.set_cookie(key='refresh_token', value='helloworld', httponly=True)
    return True

设置httponly=True确保cookie不能被JS访问。这对于刷新令牌等敏感数据非常有用。但是,如果您的数据不是那么敏感,那么您可以忽略它。

读取 cookie

from fastapi import Cookie

@app.get('/read')
async def reading(refresh_token: Optional[str] = Cookie(None)):
    return refresh_token

您可以在FastAPI docs here 上找到有关使用 cookie 作为参数的更多信息。

【讨论】:

    【解决方案2】:

    删除通配符,因为allow_credentials=True 不允许使用通配符:

    app.add_middleware(
      CORSMiddleware,
      allow_origins=['http://localhost:3000'],
      allow_credentials=True,
      allow_methods=["GET", "POST", "OPTIONS"], # include additional methods as per the application demand
      allow_headers=["Content-Type","Set-Cookie"], # include additional headers as per the application demand
    )
    

    在设置 cookie 时将 samesite 设置为 none:

    # `secure=True` is optional and used for secure https connections
    response.set_cookie(key='token_name', value='token_value', httponly=True, secure=True, samesite='none')
    

    如果客户端使用 Safari,请禁用 Preferences 中的 Prevent cros-site tracking。就是这样!

    【讨论】:

      【解决方案3】:

      在 FastAPI 中,您可以通过 response.set_cookie 设置 cookie,

      from fastapi import FastAPI, Response
      
      app = FastAPI()
      
      
      @app.post("/cookie-and-object/")
      def create_cookie(response: Response):
          response.set_cookie(key="fakesession", value="fake-cookie-session-value")
          return {"message": "Come to the dark side, we have cookies"}
      

      应该注意,虽然这些是不安全的会话,但您应该使用itsdangerous 之类的东西来创建加密会话。

      响应似乎没有发送的请求;您应该确保正在设置 cookie 对哪些 url 有效的选项。 默认情况下,它们通常是 /,这意味着您的系统可能会将它们设置为使用 CORS 设置的特定情况。

      【讨论】:

      • 这是我最初定义 cookie 的方式。当您从同一来源转向端点时,它确实有效,但不是从前端。
      • 您是否尝试从前端访问 cookie,如果是,如何访问。
      • 没有。我只需要他们在那里等待下一个请求。我检查了我的浏览器,发现它们根本没有设置。
      【解决方案4】:

      对于跨域情况 Cookie 设置,请检查以下内容??

      先决条件

      • 您的 FE、BE 服务器需要使用 https 协议相互通信。 (使用 let's encrypt 或其他服务设置 SSL 证书)
      • 确保您的域不包含端口

      后端

      服务器设置

      • 将 FE 域添加到 allow_origins
      • 设置 allow_credentials 为真
      • allowed_methods 不应是通配符(“*”)
      • allowed_headers 不应是通配符(“*”)

      Cookie 设置

      • 安全 = 真
      • httponly = 真
      • samesite = '无'
      • 列表项

      Fastapi 示例

      # main.py
      app.add_middleware(
      CORSMiddleware,
      allow_origins=settings.ALLOWED_ORIGINS,
      allow_credentials=True,
      allow_methods=["GET", "POST", "HEAD", "OPTIONS"],
      allow_headers=["Access-Control-Allow-Headers", 'Content-Type', 'Authorization', 'Access-Control-Allow-Origin'],
      

      )

      # cookie
      response = JSONResponse(content={"message": "OK"})
      expires = datetime.datetime.utcnow() + datetime.timedelta(days=30)
      response.set_cookie(
          key="access_token", value=token["access_token"], secure=True, httponly=True, samesite='none', expires=expires.strftime("%a, %d %b %Y %H:%M:%S GMT"), domain='.<YOUR DOMAIN>'
      )
      

      前端

      • 包括标题“Access-Control-Allow-Origin”:“”
      • 设置 withCredentials: true

      Axios 示例

      // post request that sets cookie
      const response = await axios.post(
                  "https://<Target Backend API>",
                  {
                      param1: "123",
                      param2: "456",
                  },
                  {
                      headers: {
                          "Access-Control-Allow-Origin": "https://<FE DOMAIN>",
                      },
                      withCredentials: true,
                  },
              );
      

      反向代理服务器(如果有)

      • 允许“OPTIONS”方法(在预检请求中浏览器检查服务器选项时需要此方法)
      • 检查是否有任何中间件阻止您的预检请求。 (例如 Nginx 基本 HTTP 身份验证可以阻止您的请求)

      重要

      如果你的FE使用dev.exmaple.com这样的子域,而你的BE也使用api.example.com这样的子域,你应该将cookie域设置为.example.com,这样子域服务才能访问根域cookie!!

      【讨论】:

        猜你喜欢
        • 2015-12-21
        • 2018-02-27
        • 2016-06-19
        • 1970-01-01
        • 2017-02-02
        • 2017-06-09
        • 2017-03-30
        相关资源
        最近更新 更多