当尝试注销时,提供的防伪令牌是为用户“XXXX”而设计的,但当前用户为空。

21

我有一个MVC 4应用程序,当表单会话过期时并且用户尝试注销时会出现问题。

例如: 超时设置为5分钟。 用户登录。 用户10分钟内未执行任何操作。 用户点击LogOff链接。 用户收到错误消息:"提供的防伪令牌是为用户“XXXX”准备的,但当前用户是“”。

然后用户必须经过一些变通才能绕过此问题,以便可以重新登录再次注销(注销用于关闭他们当天的时间记录卡)。

我认为我知道为什么会发生这种情况……但不确定如何解决它。

编辑: 我认为发生这种情况的原因是,当页面加载时,AntiForgery令牌是为当前已登录的用户生成的。但当会话过期并且他们尝试导航到注销页面时,当前用户为空而不是实际用户。因此存在不匹配,并呈现错误。


你能分享一下你的假设,为什么会出现这个错误吗? - J0e3gan
一个相关的帖子看起来值得考虑。 - J0e3gan
2
你的注销操作为什么需要防伪标记呢?看起来好像不需要。 - Craig Stuntz
2
你所描述的行为(以及需要通过它的“体操”)在我看来是正确的。如果您的注销页面为先前登录的用户执行操作,则最好确保您正在为正确的用户执行该操作... - C.B.
1
我假设你的注销操作是一个POST操作?如果它是一个GET操作,这个问题就不会发生,但是建议使用POST进行注销。 - Brian Mains
显示剩余3条评论
2个回答

30

实际上,您可以使用 IExceptionFilter 来处理它,它将重定向到 /Account/Login

public class HandleAntiForgeryError : ActionFilterAttribute, IExceptionFilter
{
    #region IExceptionFilter Members

    public void OnException(ExceptionContext filterContext)
    {
        var exception = filterContext.Exception as HttpAntiForgeryException;
        if (exception != null)
        {
            var routeValues = new RouteValueDictionary();
            routeValues["controller"] = "Account";
            routeValues["action"] = "Login";
            filterContext.Result = new RedirectToRouteResult(routeValues);
            filterContext.ExceptionHandled = true;
        }
    }

    #endregion
}

[HandleAntiForgeryError]
[ValidateAntiForgeryToken]
public ActionResult LogOff() 
{
}

你也可以使用 [HandleError(ExceptionType=typeof(HttpAntiForgeryException)...],但这需要启用 customErrors。


我简直不敢相信这还没有被标记为答案。这个答案太棒了。 - Alex Dresko
1
就我个人而言,这对我没有用。当我注销时,应用程序会在同一个错误上抛出异常。但是,异常类型是InvalidOperationException而不是HttpAntiforgeryException。这非常通用,使得简单处理此问题变得危险 :/ - Sinaesthetic
我尝试了这里建议的几乎所有方法:https://dev59.com/3mUp5IYBdhLWcg3w5Kk6 但是最终是这篇文章解决了问题。 - ckonig
@Sinaesthetic 你确定吗?我测试过了,没有任何转换的异常类型清楚地显示了一个 System.Web.Mvc.HttpAntiforgeryException。 - Javier
我遇到了同样的问题,但是在登录时。如何在登录时处理? - gaurav bhavsar

6

@cem的答案对我非常有帮助,我做了一个小改变来包括使用antiforgerytoken和过期会话的ajax调用场景。

public void OnException(ExceptionContext filterContext)
{
    var exception = filterContext.Exception as HttpAntiForgeryException;
    if (exception == null) return;

    if (filterContext.HttpContext.Request.IsAjaxRequest())
    {
        filterContext.HttpContext.Response.StatusCode = 403;
        filterContext.ExceptionHandled = true;
    }
    else
    {
        var routeValues = new RouteValueDictionary
        {
            ["controller"] = "Account",
            ["action"] = "Login"
        };
        filterContext.Result = new RedirectToRouteResult(routeValues);
        filterContext.ExceptionHandled = true;
    }
}

...并且在客户端上,您可以添加全局ajax错误处理程序来重定向到登录屏幕...

$.ajaxSetup({
    error: function (x) {
        if (x.status === 403) {
            window.location = "/Account/Login";
        }
    }
});

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