首页 > 解决方案 > 没有在 Django 中使用 Python 请求设置 CSRF 令牌

问题描述

我正在尝试使用 Python-Request 从普通 Python 脚本向 Django 视图发送 POST 请求。django 视图不是 @login_required,所以除了我的 JSON 数据之外,我唯一需要发送的是 CSRF 令牌,这是我尝试过的:

token = session.get('http://127.0.0.1:8000/myview/view')
data = json.dumps({'test': 'value'})
session.post('http://127.0.0.1:8000/myview/myview',
             data={
                 'csrfmiddlewaretoken': token,
                 'data': data})

django 视图应该只接收请求并将其打印到我的控制台:

def myview(request):
    if request.method == 'POST':
        data = request.POST.get('data')
        print(json.loads(data))
        print('received.')

    response = HttpResponse(get_token(request))
    return response

我当前代码的问题是我的控制台会抛出一个log: WARNING - Forbidden (CSRF token missing or incorrect.). 我不能使用@csrf_exempt,因为我需要它尽可能安全。有什么建议吗?提前致谢!

标签: pythondjangodjango-viewspython-requests

解决方案


为什么用户登录后会遇到 CSRF 验证失败?

出于安全原因,每次用户登录时都会轮换 CSRF 令牌。任何页面

登录前生成的表单将具有旧的、无效的 CSRF 令牌,需要重新加载。如果用户在登录后使用后退按钮或登录不同的浏览器选项卡,则可能会发生这种情况。

这也适用于 cookie。登录后,django 会向客户端发送一个新的 csrf cookie。这将存储在 client.cookies 中并替换旧的。django 服务器不保留旧令牌的任何记录,这就是为什么您会得到“CSRF 令牌丢失或不正确”的原因。回复。

您可以像以前一样从 request.cookies['csrftoken'] 访问新令牌。

import requests

LOGIN_URL = 'http://127.0.0.1:8000/myview/view'
request = requests.session()
request.get(LOGIN_URL)
# Retrieve the CSRF token first
csrftoken = request.cookies['csrftoken']
r1 = request.post(LOGIN_URL, headers={'X-CSRFToken': csrftoken},
                          allow_redirects=False))
new_csrftoken = r1.cookies['csrftoken']
data = json.dumps({'test': 'value'})
payload = {'csrfmiddlewaretoken': new_csrftoken,'data':data }

实际上,您可以直接使用客户端 cookie。这本来可以避免这个错误。当您使用 requests.session() 时,Requests 会为您跟踪 cookie。

try :
    r2 = request.post('http://127.0.0.1:8000/myview/myview', data=payload, headers={'X-CSRFToken': r1.cookies['crsftoken']})
except :
    print('error expected')

推荐阅读