[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检验的。