django接口跨域问题和解决

发布于:2024-06-27 ⋅ 阅读:(149) ⋅ 点赞:(0)

django 接口跨域问题和解决

背景

需要在一个 django 的项目中,添加一组暴露接口,使该接口可以直接接口请求而不是浏览器访问

问题现象

通过外部接口请求,比如 postman 或者 apifox 发起接口请求时,接口返回:

<!DOCTYPE html>

<html lang="en">

<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta name="robots" content="NONE,NOARCHIVE">
  <title>403 Forbidden</title>
  <style type="text/css">
    html * {
      padding: 0;
      margin: 0;
    }

    body * {
      padding: 10px 20px;
    }

    body * * {
      padding: 0;
    }

    body {
      font: small sans-serif;
      background: #eee;
      color: #000;
    }

    body>div {
      border-bottom: 1px solid #ddd;
    }

    h1 {
      font-weight: normal;
      margin-bottom: .4em;
    }

    h1 span {
      font-size: 60%;
      color: #666;
      font-weight: normal;
    }

    #info {
      background: #f6f6f6;
    }

    #info ul {
      margin: 0.5em 4em;
    }

    #info p,
    #summary p {
      padding-top: 10px;
    }

    #summary {
      background: #ffc;
    }

    #explanation {
      background: #eee;
      border-bottom: 0px none;
    }
  </style>
</head>

<body>
  <div id="summary">
    <h1>Forbidden <span>(403)</span></h1>
    <p>CSRF verification failed. Request aborted.</p>
    <p>You are seeing this message because this site requires a CSRF cookie when submitting forms. This cookie is
      required for security reasons, to ensure that your browser is not being hijacked by third parties.</p>
    <p>If you have configured your browser to disable cookies, please re-enable them, at least for this site, or for
      &#39;same-origin&#39; requests.</p>
  </div>
  <div id="info">
    <h2>Help</h2>
    <p>Reason given for failure:</p>
    <pre>
CSRF cookie not set.
</pre>

    <p>In general, this can occur when there is a genuine Cross Site Request Forgery, or when
      <a href="
https://docs.djangoproject.com/en/2.2/ref/csrf/
">Django's
        CSRF mechanism</a> has not been used correctly. For POST forms, you need to
      ensure:
    </p>

    <ul>
      <li>Your browser is accepting cookies.</li>

      <li>The view function passes a <code>request</code> to the template's <a href="
https://docs.djangoproject.com/en/dev/topics/templates/
#django.template.backends.base.Template.render"><code>render</code></a>
        method.</li>

      <li>In the template, there is a <code>{% csrf_token
%}</code> template tag inside each POST form that
        targets an internal URL.</li>

      <li>If you are not using <code>CsrfViewMiddleware</code>, then you must use
        <code>csrf_protect</code> on any views that use the <code>csrf_token</code>
        template tag, as well as those that accept the POST data.
      </li>

      <li>The form has a valid CSRF token. After logging in in another browser
        tab or hitting the back button after a login, you may need to reload the
        page with the form, because the token is rotated after a login.</li>
    </ul>

    <p>You're seeing the help section of this page because you have <code>DEBUG =
True</code> in your Django settings file. Change that to <code>False</code>,
      and only the initial error message will be displayed. </p>

    <p>You can customize this page using the CSRF_FAILURE_VIEW setting.</p>

问题原因

Django 应用在处理 POST 请求时遇到了 CSRF(跨站请求伪造,Cross-Site Request Forgery)验证失败的问题。具体来说,错误信息指出“CSRF cookie not set”,意味着请求中没有找到预期的 CSRF cookie,这是 Django 为了防止跨站请求伪造攻击而采取的安全措施

解决方案

Django 默认并不允许跨域请求,为了允许从 Postman 或其他外部域名发起的 POST 请求,你需要设置 CORS 策略

使用 django-cors-headers

  1. 安装

pip install django-cors-headers

注意和 django 的版本对应,不同版本的 django-cors-headers 所依赖的 django 版本不同,具体可以参考:https://pypi.org/project/django-cors-headers/3.0.0/

  1. 注册

corsheaders 添加到 INSTALLED_APPS 列表中。
添加 CORS 中间件到中间件列表中,并确保它位于其他中间件之前,以便它可以最早处理 CORS 头部。
可以全局允许所有来源,或者指定允许的来源列表。

# settings.py
INSTALLED_APPS = [
    # ...
    'corsheaders',
    # ...
]

MIDDLEWARE = [
    # CORS Headers middleware should be placed as high as possible
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    # ...
]

CORS_ORIGIN_ALLOW_ALL = True  # 这将允许所有来源的请求。在生产环境中,你可能需要更严格的控制,比如列出具体的允许来源。
# 或者,你可以指定允许的源列表:
# CORS_ALLOWED_ORIGINS = [
#     "http://localhost:3000",
#     "https://example.com",
# ]

注意的就是 cors-headers 的中间件CorsMiddleware 在注册时必须放在 django-common 中间件的前一个。

但是这种方式我最终没成功,目前原因没找到。

使用装饰器 @csrf_exempt

  1. 导入
from django.views.decorators.csrf import csrf_exempt
  1. 使用
@csrf_exempt
def notify_success(request):
    # 你可以根据需要处理请求数据,这里简化处理直接返回成功信息
    return HttpResponse('success')

@csrf_exempt
def notify_failure(request):
    # 同样,这里简化处理直接返回失败信息
    return HttpResponse("failure")

使用该方式成功,apifox 再发起请求后,请求成功,跨域问题不存在。


网站公告

今日签到

点亮在社区的每一天
去签到