在 ajax 请求中处理会话超时的最佳方法是啥?

Posted

技术标签:

【中文标题】在 ajax 请求中处理会话超时的最佳方法是啥?【英文标题】:What's the best way to handle session timeouts in ajax requests?在 ajax 请求中处理会话超时的最佳方法是什么? 【发布时间】:2012-03-30 11:35:42 【问题描述】:

考虑这个 Django 视图,它将获取与当前用户关联的项目列表:

@login_required
def list_items(request, page_number=0):
    items = Paginator(request.user.items, 5).page(page_number).object_list
    return HttpResponse(cjson.encode(items))

显然,它想使用login_required 装饰器来限制登录用户对视图的访问。

当未经身份验证的用户尝试访问视图时,login_required 会做什么?它向settings.LOGIN_URL 返回一个HttpResponseRedirect

考虑这个调用视图的 javascript 代码:

var getPage = function(pageNumber) 
    $.ajax(
        url: "/list_items/" + pageNumber + "/",
        success: function(data) 
            $("#list_container").html(formatData(data))
        
    );
;

假设settings.SESSION_COOKIE_AGE = 60 秒。

如果用户转到第 1 页,阅读了 61 秒,然后单击第 2 页的按钮,Django 的login_required 装饰器将检测到会话不再活动,并返回一个HttpResponseRedirect(settings.LOGIN_URL),它将导致 success 回调获取 HTML 登录页面,而不是 JSON 编码的列表。

This is where it happens.It's called by user_passes_test here.

处理这个问题的最佳方法是什么?

以下是我想到的一些事情:

1. success 回调应该检查响应,并查看它是否通过任何方式获取登录页面(检查内容类型是否为 html,检查内容等)。但这意味着我们必须使用回调包装器来包装所有 AJAX 调用,如下所示:

    $.ajax(
        url: "/list_items/" + pageNumber + "/",
        success: sessionExpiryCallbackWrapper(function(data) 
            $("#list_container").html(formatData(data))
        )
    );

但这很丑陋,开发人员可能会忘记在任何地方都这样做。

2. 使用$.ajaxComplete 处理所有请求。

    $.ajaxComplete(globalCompleteCallback);
    $.ajax(
        success: successCallback,
        complete: completeCallback
    );

但这是调用顺序:

    successCallback(); // success is called before complete
    completeCallback();
    globalCompleteCallback(); // this is called after the local callback

所以我们只在 successCallback 失败后捕获重定向,并且可能由于接收到的数据无效而导致 JS 错误。

3. 如果login_required 在 AJAX 请求上返回 403:

    if not user.is_authenticated():
        if request.is_ajax():
            # send 403 to ajax calls
            return HttpResponse403("you are not logged in")
        else:
            # regular code path
            return HttpResponseRedirect(settings.LOGIN_URL)

login_required 只使用user_passes_test,它不会这样做。

user_passes_test 里面有很多功能,所以重新实现它不是一个好主意。

处理 AJAX 调用超时的最佳方法是什么?

【问题讨论】:

对不起,我要对你进行所有的哲学研究。会话超时的最初目的是防止服务器持有大量资源或不得不对每个请求执行昂贵的操作。我们现在有客户端框架来完成大部分工作。那么为什么还需要短会话超时呢? @DaveMethvin 需要会话超时来缩小会话劫持的窗口,无论是 a) 有线/空中还是 b) 某人在稍微 AFK 后转到其他人的计算机上。 不过,这个问题似乎很容易解决,只需让客户端会话超时时间短于服务器会话超时时间。如果是这种情况,客户端应该很少会在没有有效会话的情况下在服务器上发出请求。另外,这意味着您可以发布一个漂亮的客户端发起的“您的会话已超时”消息。 @DaveMethvin 想法不错,但是如果您的页面显示静态内容并且仅在用户执行某些操作而不是与静态内容交互时才会刷新,那么显示登录会很好形成并重试所需的 AJAX 调用。 【参考方案1】:

您可以使用 http://amplifyjs.com/ 之类的东西,它可以让您为 AJAX 调用编写一个很好的包装器,然后使用其 data mapping 功能在执行 AJAX 调用之前检查用户是否仍然登录。

通过这种方式,您可以拥有一个客户端计时器,将用户设置为注销状态并提供提示,因此无需在每次 AJAX 调用之前进行登录检查。

或者,您可以使用自定义decoder,它要求用户登录并在用户退出时重试 AJAX 调用。它需要存储所有 xhr 数据和调用它的回调,直到用户登录。

【讨论】:

【参考方案2】:

我会通过让你的会话超时方法检查它是否被 AJAX 请求来处理它。如果是 ajax,则返回带有空 json 字符串的 401 未授权(或 403 禁止或任何有意义的状态)状态代码。接下来,在您的 javascript 中,绑定一个全局 ajaxError 处理程序,该处理程序检查该状态代码并适当地处理它。

【讨论】:

这就是我最终所做的,但我必须为return HttpResponse401,3 if request.is_ajax() else RedirectToLoginPage() 编写一个新的login_required 装饰器,而不仅仅是重定向。

以上是关于在 ajax 请求中处理会话超时的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Grails,在 AJAX 调用中处理会话超时

如何处理 AJAX 请求中的会话超时

Spring 安全性 + Ajax 会话超时问题

如何使用JQuery和ColdFusion处理会话超时警告?

JSF/PrimeFaces ajax 请求上的会话超时和 ViewExpiredException 处理

Spring 3.1:处理会话超时