使用Django中的@csrf_exempt装饰器后,request.session总是为空。

9

我在Django中遇到了问题,非常希望有人能够帮忙。

我需要为第三方API创建一个入口点。因此,我创建了一个视图并用@csrf_exempt进行了装饰。

现在的问题是,我无法访问以前设置的任何会话变量。 编辑 - 我设置了多个会话变量,例如用户电子邮件以知道是否已经登录。在调用第三方API之前,我能够使用会话。当第三方API发送响应时,他们不会发送CSRF令牌,因此我免除了该视图的CSRF。一旦我收到有效的响应,我想要更新我的数据库。为此,我需要知道用户的电子邮件地址,但由于我没有会话变量,所以失去了这个信息。

ppConfirmPaymentProcess是另一个处理此第三方API发送的POST数据的功能。 一切正常,csrf_exempt也正常工作,但我无法通过此请求执行request.session["foo"]。有人能帮忙吗?

@csrf_exempt
def ppConfirmPayment(request):
    print(request.session, "=======================================")
    for key, value in request.session.items():
        print('{} => {}'.format(key, value))
    return ppConfirmPaymentProcess(request)

1
现在的问题是我无法访问之前设置的任何会话变量。你是怎么做到的? - Kurohige
1
API调用程序是否注意在所有调用中发送相同的“sessionid” cookie?如果没有,那么Django将把每个调用视为新会话。 - John Gordon
1
我之前设置的变量在哪里?是在你的会话中,还是在API用户中? - Basic
1
你的API入口是一个webhook接收器吗?在这种情况下,API调用将直接来自您的支付提供商而不是您的客户端,因此您将无法访问客户端的会话。支付提供商将在创建付款请求时为您提供一个ID,将其存储在数据库中以及您所需的任何其他信息一起使用它进行关联。 - Andee
我已经为你的情况创建了一个类似的场景(我的示例运行良好),这与csrf_exempt无关,请您提供有关如何设置会话变量的其他信息。 - scriptmonster
显示剩余2条评论
4个回答

1

request.session将始终为空,因为它只能由您的系统初始化,并且一旦连接关闭,会话就会被销毁。对于新连接,将设置新会话。由于您的API端点直接由第三方API触发,而没有触发任何其他端点,因此第三方无法设置会话。因此,request.session为空。


尝试将信息存储在request.POST中,而不是request.session,这是从第三方获取信息的更优化的方式。


1

请参阅文档:https://docs.djangoproject.com/en/3.0/topics/http/sessions/

Django将会存储以Session Key为标记的Session数据,如下所示:
{"session_key":sdfkjdsbks, "user_email":"abc@df.com"}

当您存储用户电子邮件并且需要检查是否获得了相同的Session Key时,请打印并复制request.session.session_key

如果没有获得相同的Session Key,则可以使用已复制的Session Key强制在视图中加载先前的Session。

from importlib import import_module
from django.conf import settings

@csrf_exempt
def ppConfirmPayment(request):

    engine = import_module(settings.SESSION_ENGINE)

    # copy the old_session_key value from request when you saved user email
    request.session = engine.SessionStore(old_session_key)

    print(request.session, "=======================================")
    for key, value in request.session.items():
        print('{} => {}'.format(key, value))
    return ppConfirmPaymentProcess(request)


如果这个方法可行,那么你可以将session_key发送给第三方API,并请求他们在后续调用中将相同的session key放入cookie或数据中。然后捕获session key并强制在你的视图中加载该session。

嗨,Rahul,这似乎是一个可能的解决方案,我会尝试并让你知道。谢谢。 - GKV

0

我使用Django自身解决了这个问题。没有操作session-id或者与数据库的交互。

步骤1: 调用第三方API

@login_required
def thirdPartyAPICall(request):
    #do some stuff and send a request to 3rd party

步骤2:在视图中接收第三方的回调。请注意,我使用了csrf_exempt而不是login_required,以便第三方可以发送请求到我的应用程序,而无需CSRF令牌和会话。这对于他们来说就像是进入我的应用程序的入口点。

在这个callBackView中执行一些操作,并检查这是否确实是来自第三方的有效响应,或者有人试图黑客攻击您的系统。

例如,检查CHECKSUMTXNID等,然后创建一个响应字典,并使用HttpResponseRedirect将另一个HTTP响应发送到我的应用程序内的另一个资源,然后我传递相关的GET参数给它。

这个特定的步骤恢复了我的先前会话,现在我有了来自第三方处理请求所需的相关数据,同时也得到了我的session

@csrf_exempt
def callBackView(request):
     if request.POST["CHECKSUM"] == myCalCulatedCheckSum:
          foo = True
     else:
          foo = False
     return HttpResponseRedirect("TEST.HTML" +"/" + str(foo))

我最喜欢这种方法,因为正如我之前提到的,我们不需要存储会话,Django 会为我们完成。


-1

第三方API可能没有以已登录的用户身份发送响应,肯定不是启动交易的用户。

您需要跟踪您向API发出的请求及其关联的用户,可能在数据库中。当您从API收到响应时,您需要将其与这些记录匹配,加载相关用户信息并进行适当的更新。

第三方API的响应与用户会话完全分离;这意味着它可能会在单独的线程、进程甚至服务器上处理(取决于您的站点设置方式),因此您需要使用一些跨越这些边界的机制,以便让用户了解付款结果(这意味着要么是数据库,要么是您为此设置的其他内容)。


这是我问题中提到的内容。这是我想要解决的基本问题。 - GKV
是的,您需要使用回调中的信息来确定它响应的是哪个请求。如果不知道API返回什么,我们无法真正帮助您解决这个问题,但希望会有某种“请求ID”可以匹配。一旦知道了是哪个请求,就可以从那里查询数据库。 - Jiří Baum

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接