Asp.net MVC - 如何检查 Ajax 请求的会话过期

Asp.net MVC - How to check session expire for Ajax request

我们在整个应用程序中使用 Ajax 调用 - 试图找到一个全局解决方案,如果在尝试执行任何 Ajax 请求时会话已经过期,则重定向到登录页面。我已经编写了以下解决方案,从这个 post - Handling session timeout in ajax calls

获得帮助

不确定为什么在我的护理事件中 "HandleUnauthorizedRequest" 没有被解雇。

自定义属性:

 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class CheckSessionExpireAttribute :AuthorizeAttribute
    {
        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            if (filterContext.HttpContext.Request.IsAjaxRequest())
            {
                var url = new UrlHelper(filterContext.RequestContext);
                var loginUrl = url.Content("/Default.aspx");

                filterContext.HttpContext.Session.RemoveAll();
                filterContext.HttpContext.Response.StatusCode = 403;
                filterContext.HttpContext.Response.Redirect(loginUrl, false);
                filterContext.Result = new EmptyResult();
            }
            else
            {
                base.HandleUnauthorizedRequest(filterContext);
            }

        }

    }

在控制器操作中使用 Above 自定义属性如下:

 [NoCache]
 [CheckSessionExpire]
 public ActionResult GetSomething()
 {
  }

AJAX调用(JS部分):

function GetSomething()
{
   $.ajax({
        cache: false,
        type: "GET",
        async: true,
        url: "/Customer/GetSomething",
        success: function (data) {

        },
        error: function (xhr, ajaxOptions, thrownError) {

        }
}

Web Config 身份验证设置:

  <authentication mode="Forms">
      <forms loginUrl="default.aspx" protection="All" timeout="3000" slidingExpiration="true" />
    </authentication>

我尝试通过在进行 ajax 调用之前删除浏览器烹饪来检查它,但是事件 "CheckSessionExpireAttribute " 没有被触发 - 请知道任何想法。

谢谢,

@保罗

我检查并测试了代码,看起来很清楚..问题是ajax调用错误..

我修复了 Ajax 代码,试试这个..

function GetSomething() {
        $.ajax({
            cache: false,
            type: "GET",
            async: true,
            url: "/Customer/GetSomething",
            success: function (data) {

            },
            error: function (xhr, ajaxOptions, thrownError) {

            }
        });
    }

在HttpContext.Request.IsAjaxRequest()

请参阅这篇相关文章,了解为什么 Ajax 请求可能无法被识别。

XMLHttpRequest() not recognized as a IsAjaxRequest?

It looks like there is a dependency on a certain header value (X-Requested-With) being in the request in order for that function to return true.

您可能想要捕获并查看您的流量,并headers到服务器以查看浏览器是否确实正确地发送了这个值。

但是,你确定它会碰到那行代码吗?你可能还想调试 断点 并查看设置了哪些值。

在 Session 与身份验证

授权和Session超时并不总是完全相同。实际上可以授予比 session 更长期限的授权,如果缺少 session,只要他们已经获得授权,就重建它。如果您发现 session 上有一些您将丢失且无法重建的东西,那么也许您应该将其移到其他地方,或者另外将其保留在其他地方。

表单身份验证 cookie 默认在 30 分钟 后超时。 Session 超时默认为 20 分钟.

Session timeout in ASP.NET

HandleUnauthorizedRequest not overriding

我认为这只是一个 client-side 问题。 在 Web 服务器中,您可以只对操作或控制器使用经典的 Authorize 属性。 这将验证请求是否已通过 身份验证(如果存在有效的身份验证 cookie 或授权 header),如果未通过身份验证则设置 HTTP 401。

Note: a session will automatically be recreated if you don't send authorization info in the request, but the request will not be authorized

解决方案

然后 javascript 客户端您 必须 处理重定向(浏览器会自动执行,但对于 ajax 您需要手动执行)

$.ajax({
    type: "GET",
    url: "/Customer/GetSomething",
    statusCode: {
       401: function() {
          // do redirect to your login page
          window.location.href = '/default.aspx'
       }
    }
});

很抱歉地说:您需要的解决方案不可能。原因是:

  • 要将用户重定向到登录页面,我们有两种方法:在服务器重定向,在客户端重定向
  • 在你的例子中,你使用的是 Ajax 所以我们只有一种方法:在客户端重定向(原因基本上是,Ajax 意味着 send/retrieve 数据 to/from 服务器。所以不可能在服务器重定向)
  • 接下来,在客户端重定向。 Ajax 需要来自服务器的信息说 "redirect user to login page" 而全局检查会话方法必须是 return Redirect("url here").
  • 显然,全局检查会话方法不能return 2 type (return Redirect(), return Json,Xml,Object,or字符串)

毕竟我建议:

  • 解决方案 1:不要使用 ajax
  • 解决方案 2:您可以使用 ajax,但请检查非全局服务器上的会话超时方法。意味着你必须多个实现(ajax调用的数量=实现的数量)

如果我答对了问题(即使我没有答对,无论如何谢谢,帮助我解决了我自己的问题),你想要避免的是让你的登录页面加载到一个元素中,这个元素应该是通过 Ajax 显示不同的视图。或者在 Ajax 表单 post.

期间获得 exception/error 状态代码

所以,简而言之,注释 class 将需要重写 2 个方法,而不仅仅是 HandleUnauthorizedRequest,它会重定向到一个 JsonResult Action 来生成参数让你的 Ajax 函数知道该做什么。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class SessionTimeoutAttribute : AuthorizeAttribute
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        IPrincipal user = filterContext.HttpContext.User;
        base.OnAuthorization(filterContext);
        if (!user.Identity.IsAuthenticated) {
            HandleUnauthorizedRequest(filterContext);
        }
    }

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            filterContext.Result = new RedirectToRouteResult(new
            RouteValueDictionary(new { controller = "AccountController", action = "Timeout" }));
        }
    }
}

然后在你的authentication Action中设置这个annotation,这样每次调用它的时候,它就会知道请求来自哪里,应该给什么样的return。

[AllowAnonymous]
[SessionTimeout]
public ActionResult Login() { }

然后您的重定向 Json 操作:

[AllowAnonymous]
public JsonResult Timeout()
{
    // For you to display an error message when the login page is loaded, in case you want it
    TempData["hasError"] = true;
    TempData["errorMessage"] = "Your session expired, please log-in again.";

    return Json(new
    {
        @timeout = true,
        url = Url.Content("~/AccountController/Login")
    }, JsonRequestBehavior.AllowGet);
}

然后在您的客户端函数中(我有幸将其写为 $.get() 而不是 $.ajax():

$(document).ready(function () {
    $("[data-ajax-render-html]").each(function () {
        var partial = $(this).attr("data-ajax-render-html");
        var obj = $(this);

        $.get(partial, function (data) {
            if (data.timeout) {
                window.location.href = data.url;
            } else {
                obj.replaceWith(data);
            }
        }).fail(function () {
            obj.replaceWith("Error: It wasn't possible to load the element");
        });
    });
});

这个函数用这个data-ajax-render-html属性替换了html标签,里面包含了你要加载的View地址,但是你可以通过改变[=18]来设置在标签内部加载=] 对于 html() 属性.