[未解决]将xiaoluo-3d部署到公网后,出现所有POST都报403错误


[TOC]

一、前言

1。我的Django APP(omserver)有在settings.py里启用csrf_token middleware

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    # 'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'corsheaders.middleware.CorsMiddleware',
]

2。在我的所有form里都有加{% csrf_token %}标签
代码里有两种方式的POST,
1)有form: 直接将整个form serialize(),然后 ajax submit

    let data = $(this).serialize();
    $.ajax({ type: 'POST', url: '/omserver/character/role/create', data: data, success: function(response) {
          console.log(response);
          alert("新增虚拟人成功!");
          window.location.href = "character_store.html";
        },
        error: function(xhr, status, error) {
            console.error(error);
            alert("新增虚拟人失败!" + error);
        }
    });

2)没有form:

    var data = new FormData();
    data.append("id", $('#id').val());
    data.append("action_name", $('#action_name').val());
    data.append("action_type", $('#action_type').val());
    ...

    var csrftoken = getCookie('csrftoken');
    $.ajaxSetup({
          beforeSend: function(xhr, settings) {
              if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
                  xhr.setRequestHeader("X-CSRFToken", csrftoken);
              }
          }
      });

    $.ajax({type: 'POST', url: myurl, data: data, cache: false, processData: false, contentType: false, async: true,
        success: function (r) {
          modalDlg(false);
          console.log("response:"+ r);
          if (r['code'] == 200) {
            showtip("新增模型成功!");
            window.location.href = "/omserver/character_actions.html";
          } else {
            showtip("新增模型失败!" + r['response']);
          }
        }, error: function (r) {
          console.log(r.responseText); 
          showtip("新增模型失败1!" + r.responseText);
        },
        failure: function (r) {
          console.log(r.responseText);
          showtip("新增模型失败2!" + r.responseText);
        }
    })

3。在开发环境跑所有功能都OK。

4。开发环境和部署环境的一个差别
1)开发环境访问后台是直接连接后台的django地址:http://localhost:8000/omserver
2)部署环境访问后台是跟前端共用了一个域名:https://x.rg4.net ,然后这个域名再通过nginx反向代码分别跳转到前台(http://localhost:3000)和后台(http://localhost:8000/omserver)

二、各种尝试

尝试1:怀疑是不是过nginx后,cookie丢失?nginx反向路由加一些头

    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header Cookie $http_cookie; # 转发所有cookie

结果:没用,403依旧。

尝试2:怀疑csrf token没传到django?在我的app里加一个 middleware,拦截所有的request,并将其内容打出来以确认 django有没有收到前端传过来的csrf token

1)先在我的app omserver下创建一个middleware.py,然后加入以下代码

class PrintRequestMiddleware(MiddlewareMixin):
    def process_request(self, request):
        print("Request Method:", request.method)
        if request.method == 'POST':
            csrf_token = request.META.get('CSRF_COOKIE')
            print(f"Request POST:csrf={csrf_token}, data={request.POST}")
        else:
            print("Request body:", request.body)

2)将这个middleware加入到settings.py

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    # 'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'corsheaders.middleware.CorsMiddleware',
    'omserver.middleware.PrintRequestMiddleware',
]

结果:django有收到前端传过来的csrf token,但就是403

[DEBUG][2024-04-22 06:39:41,500][http_protocol.py:159]      HTTP b'POST' request for ['127.0.0.1', 40546]
Request Method: POST
Request POST: <QueryDict: {'csrfmiddlewaretoken': ['Big7SE0zGPlfi04xSk97Rl8ETy7Tfan0XGojsxqAuuZzg9p2hIDxXhw9WhIw5Xu2'], 'role_name': ['Joe'], 'gender': ['1'], 'persona': ['{Angela}是一个专业的客服人员。她有以下一些特点:有耐心:能够倾听客户的需求和问题,并且在解决复杂问题时保持冷静,不急躁。有同理心:理解和尊重客户的情感状态,能够站在客户的角度思考问题,具备换位思考的能力。沟通能力:拥有出色的口头和书面沟通技巧,能用清晰、简洁的语言表达意思,同时善于倾听和解读客户的话语背后的真实需求。抗压性:客服经常会面临各种挑战,包括处理客户投诉、解决突发问题等,因此需要具备较强的心理承受能力和情绪管理能力。积极乐观:保持积极的工作态度,能够在困难情况下保持乐观,传递正能量给客户。细致入微:对于细节的关注度高,无论是处理订单还是解答问题都能做到细心周到。解决问题的能力:迅速而准确地识别和解决问题,有时候需要灵活运用政策和程序来找到最佳解决方案。专业知识:对所在行业的专业知识和公司产品有深入了解,以便准确解答客户疑问。忍耐与宽容:在面对各种客户类型时都能保持谦逊和礼貌,即便面对棘手的客户也能表现出极大的包容。服务导向:始终以客户需求为中心,致力于提供超越客户期望的服务体验。责任心:对客户承诺的事情负责到底,不轻易做出不能兑现的承诺,并且勇于承担错误带来的后果。她的这些性格特质和专业素养结合在一起,有助于客服人员有效地建立和维护客户关系,提高客户满意度和忠诚度。'], 'personality': ['耐心,有同理心,抗压能力强,积极乐观,情绪稳定'], 'scenario': [''], 'examples_of_dialogue': ['你将作为一个客服人员与人进行对话模拟,是你一个资深、专业的客服人员,耐心,有同理心,具备换位思考的能力,具备较强的心理承受能力和情绪管理能力,对客户承诺的事情负责到底,不轻易做出不能兑现的承诺,并且勇于承担错误带来的后果。以下为一些示例。【欢迎语】您好,我是罗索实验室的智能客服助手,很高兴为您服务!请问有什么可以帮助您的吗?【咨询产品相关问题】您好,请问您对我们哪款产品有疑问或需要了解更多信息呢?如果您有关于产品功能、使用方法、价格等方面的问题,请随时向我提问。【查询订单状态】如果您想查询订单状态,请提供您的订单号,我会立即为您查询。【售后服务】若您遇到了产品质量问题,或者需要退换货、维修等售后服务,请告诉我具体的情况和您的诉求。【投诉与建议】如果您有任何不满或有宝贵的意见和建议,欢迎您向我反馈,我们会尽快处理并改进。【结束对话】感谢您的咨询,如有其他疑问请随时联系我们。期待再次为您服务,祝您生活愉快!【无法解决问题时】非常抱歉,您的问题暂时超出了我的处理范围,我会立刻转交给人工客服专员为您解答,敬请稍候。'], 'custom_role_template_type': ['zh'], 'examples_of_prompt': [''], 'scene_id': ['background/校园.jpg'], 'model_id': ['model/character-joe.vrm'], 'voice_id': ['zh-CN-YunjianNeural']}>
[DEBUG][2024-04-22 06:39:41,561][http_protocol.py:254]      HTTP 403 response started for ['127.0.0.1', 40546]

这里打印出来的csrfmiddlewaretoken值,与我在前面javascript ajax post时打印出来的值是一毛一样的,为什么报错?

尝试3:分析csrf.py代码,将校验过程打印出来

1)拿到检验错误原因为:
{“detail”:”CSRF Failed: Origin checking failed – https://x.rg4.net does not match any trusted origins.”}
2)重新修改settings.py,将前面禁用掉的csrf middleware重新打开

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'corsheaders.middleware.CorsMiddleware',
    'omserver.middleware.PrintRequestMiddleware',
]

同时在settings.py里加入

CORS_ALLOW_ALL_ORIGINS = True  # 允许所有来源
CORS_ALLOW_HEADERS = ['*']  # 允许所有请求头
CORS_ALLOW_METHODS = ['GET', 'POST', 'PUT', 'DELETE']  # 允许的请求方法

CSRF_TRUSTED_ORIGINS = [ 'http://localhost:8000', 'https://x.rg4.net', ]
ALLOWED_HOSTS = [ 'localhost', 'x.rg4.net']
CORS_ORIGIN_WHITELIST = ['http://localhost:8000', 'https://x.rg4.net' ]

这个时候报错变成了

{"detail":"CSRF Failed: CSRF token from POST incorrect."}

注:中间有碰到一个新问题,刷新前端界面报错

Refused to display 'https://x.rg4.net/omserver' in a frame because it set 'X-Frame-Options' to 'deny'.

修改settings.py,并在其中加入

X_FRAME_OPTIONS = 'SAMEORIGIN'

此问题消失。

回到CSRF报错

{“detail”:”CSRF Failed: CSRF token from POST incorrect.”}

尝试4:将nginx的跳转路由增加或删除一些头

  • 增加:同尝试1,每个跳转都加X-Forwarded-For和Cookie头,依旧是CSRF Failed: CSRF token from POST incorrect.
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header Cookie $http_cookie; # 转发所有cookie
  • 删除:将X-Forwarded-For和Cookie头从配置文本里全都删除,依旧是CSRF Failed: CSRF token from POST incorrect.

因为我在“尝试2”那里加了一个middleware,可以将后端收到的整个request打印出来,而后端打印出来的csrfmiddlewaretoken值,与我在前端javascript ajax post时打印出来的值是一毛一样的,就是不知道为什么报CSRF Failed: CSRF token from POST incorrect? 我能想到的唯一的可能性是前端渲染网页的时候,这个csrf token已经过期了(我没有点返回网页刷新网页,再post也),但是为什么过期了?怎么解决? I have no idea.

三、当前结果:暂放弃

一整个周末都浪费在这一个问题上了,至今未解决,时间关系,暂时先不管这个问题了,直接将settings.py里所有与csrf和CORS相关的中间件全部禁用,先跳过这个问题。
所以目前部署在https://x.rg4.net/omserver 上的代码是不做csrf检验的。

Leave a comment

Your email address will not be published. Required fields are marked *