Asp.net mvc 如果用户向仅POST操作提交表单并且其身份验证超时,则登录后重定向将导致404错误

Asp.net mvc 如果用户向仅POST操作提交表单并且其身份验证超时,则登录后重定向将导致404错误,asp.net-mvc,forms-authentication,http-post,Asp.net Mvc,Forms Authentication,Http Post,我有一个使用表单身份验证的MVC应用程序,我收到了404个错误。发生的情况是,当用户的身份验证超时时,用户恰好将表单提交给一个仅发布的操作,并且他们被重定向到登录页面。登录后,使用GET将它们重定向回原始URL,这将导致404错误,因为操作是POST only 我有两个问题: 我的想法是通过某种方式检测被重定向到的操作是否是一个仅发布的操作,然后重定向到主页。我该怎么做呢 理想情况下,应用程序会记住发布的值,并通过POST将它们提交到原始URL,但我不知道如何绕过表单身份验证来做到这一点,我怀疑

我有一个使用表单身份验证的MVC应用程序,我收到了404个错误。发生的情况是,当用户的身份验证超时时,用户恰好将表单提交给一个仅发布的操作,并且他们被重定向到登录页面。登录后,使用GET将它们重定向回原始URL,这将导致404错误,因为操作是POST only

我有两个问题:

  • 我的想法是通过某种方式检测被重定向到的操作是否是一个仅发布的操作,然后重定向到主页。我该怎么做呢

  • 理想情况下,应用程序会记住发布的值,并通过POST将它们提交到原始URL,但我不知道如何绕过表单身份验证来做到这一点,我怀疑这可能会导致安全漏洞。这是一个好主意吗?如果是,怎么做


  • 一个简单的修复方法是创建一个GET-only操作,该操作与POST-only操作同名,只是重定向到主页。创建一个在登录后恢复表单发布的解决方案需要花费大量的工作才能获得最小的收益

    更新:


    关于创建所有这些GET操作的工作量。。一个更优雅的选择是专门为此场景创建一个名为
    HttpPostOrRedirectAttribute
    的属性,您可以使用它来修饰这些仅发布的操作,而不是使用
    httpposattribute
    。它的行为是接受帖子,但不是抛出404,而是对其他动词执行重定向。

    正是遇到了这个问题,我们为重定向到索引的帖子创建了[HttpGet]操作;用户丢失了他们输入的数据-所以这不是一件好事-但这是我们解决问题的快捷方法。

    做一个GET操作,重定向到包含原始表单的页面怎么样

    您可以向ModelView数据中添加一些额外的信息,以指示应该向用户显示一条消息。这个消息应该是这样说的

    您在注销时提交了此表单-现在您已登录,是否继续提交

    或者,如果确实需要,ModelViewData中的额外信息可能会导致表单自动提交


    正如您所说,如果您有很多表单页面,这是一项相当多的额外工作,但应该有某种方式来封装行为以供重用。

    我创建了一个动作过滤器,作为上述响应的结果,我将把它留给后人。它将任何指定的参数从尝试的操作传递到重定向操作

    public class HttpPostOrRedirectAttribute : ActionFilterAttribute
    {
        public string RedirectAction { get; set; }
        public string RedirectController { get; set; }
        public string[] ParametersToPassWithRedirect { get; set; }
    
        public HttpPostOrRedirectAttribute(string redirectAction)
            : this(redirectAction, null, new string[] { })
        {
        }
    
        public HttpPostOrRedirectAttribute(string redirectAction, string[] parametersToPassWithRedirect)
            : this(redirectAction, null, parametersToPassWithRedirect)
        {
        }
    
        public HttpPostOrRedirectAttribute(string redirectAction, string redirectController, string[] parametersToPassWithRedirect)
        {
            RedirectAction = redirectAction;
            RedirectController = redirectController;
            ParametersToPassWithRedirect = parametersToPassWithRedirect;
        }
    
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (filterContext.HttpContext.Request.HttpMethod == "POST")
            {
                base.OnActionExecuting(filterContext);
            }
            else
            {
                string redirectUrl = GetRedirectUrl(filterContext.RequestContext);
                filterContext.Controller.TempData["Warning"] = "Your action could not be completed as your"
                    + " session had expired.  Please try again."; 
                filterContext.Result = new RedirectResult(redirectUrl);               
            }
        }
    
        public string GetRedirectUrl(RequestContext context)
        {
            RouteValueDictionary routeValues = new RouteValueDictionary();
            foreach (string parameter in ParametersToPassWithRedirect)
            {
                if(context.RouteData.Values.ContainsKey(parameter))
                    routeValues.Add(parameter, context.RouteData.Values[parameter]);
            }
            string controller = RedirectController 
                ?? context.RouteData.Values["controller"].ToString();
            UrlHelper urlHelper = new UrlHelper(context);
            return urlHelper.Action(RedirectAction, controller, routeValues);           
        }
    }
    
    要使用,只需将相关操作上的
    HttpPost
    过滤器替换为
    HttpPostOrRedirect
    ,即可:

    [HttpPostOrRedirect("Display", "User", new[] { "id", "param1", "param2" })]
    public ActionResult Delete(User user, int param1, string param2)
    {
        ...
    }
    

    下面是@stusherwin answer的改进版本,支持ValidateAntiForgeryTokenMVC区域

    您的POST操作可能具有ValidateAntiForgeryToken属性以防止CSRF攻击。在这种情况下,将始终首先执行ValidateAntiForgeryToken筛选器,因为它是授权筛选器。因此,我们还需要使HttpPostOrRedirectAttribute成为一个授权过滤器。否则,将引发未找到防伪令牌的异常

    另一个改进是向MVC区域添加重定向

        public class HttpPostOrRedirectAttribute : FilterAttribute, IAuthorizationFilter
        {
            public string RedirectAction { get; set; }
            public string RedirectController { get; set; }
            public string RedirectArea { get; set; }
            public string[] ParametersToPassWithRedirect { get; set; }
    
            public HttpPostOrRedirectAttribute(string redirectAction)
                : this(redirectAction, null, new string[] { })
            {
            }
    
            public HttpPostOrRedirectAttribute(string redirectAction, string[] parametersToPassWithRedirect)
                : this(redirectAction, null, parametersToPassWithRedirect)
            {
            }
    
            public HttpPostOrRedirectAttribute(string redirectAction, string redirectController, string[] parametersToPassWithRedirect)
            {
                RedirectAction = redirectAction;
                RedirectController = redirectController;
                ParametersToPassWithRedirect = parametersToPassWithRedirect;
            }
    
            public HttpPostOrRedirectAttribute(string redirectAction, string redirectController, string redirectArea)
            {
                RedirectAction = redirectAction;
                RedirectController = redirectController;
                RedirectArea = redirectArea;
            }
    
            public HttpPostOrRedirectAttribute(string redirectAction, string redirectController, string redirectArea, string[] parametersToPassWithRedirect)
            {
                RedirectAction = redirectAction;
                RedirectController = redirectController;
                RedirectArea = redirectArea;
                ParametersToPassWithRedirect = parametersToPassWithRedirect;
            }
    
            public void OnAuthorization(AuthorizationContext filterContext)
            {
                if (filterContext.HttpContext.Request.HttpMethod == "POST")
                    return;
    
                string redirectUrl = GetRedirectUrl(filterContext.RequestContext);
                filterContext.Controller.TempData["Warning"] = "Your action could not be completed as your"
                    + " session had expired.  Please try again.";
                filterContext.Result = new RedirectResult(redirectUrl);
            }
    
            public string GetRedirectUrl(RequestContext context)
            {
                RouteValueDictionary routeValues = new RouteValueDictionary();
                foreach (string parameter in ParametersToPassWithRedirect)
                {
                    if (context.RouteData.Values.ContainsKey(parameter))
                        routeValues.Add(parameter, context.RouteData.Values[parameter]);
                }
    
                if (RedirectArea.IsNotEmpty())
                    routeValues.Add("area", RedirectArea);
    
                string controller = RedirectController
                    ?? context.RouteData.Values["controller"].ToString();
                UrlHelper urlHelper = new UrlHelper(context);
                return urlHelper.Action(RedirectAction, controller, routeValues);
            }
        }
    
    以下是如何将其与ValidateAntiForgeryToken属性一起使用并重定向到管理区域的示例:

    [HttpPostOrRedirect("Display", "User", "Admin", new[] { "id", "param1"}, Order = 0)]
    [ValidateAntiForgeryToken(Order = 1)]
    public ActionResult Delete(User user, int param1, string param2)
    {
        ...
    }
    

    我没想到!虽然有很多只发布的操作,但这需要添加很多新操作。好主意。这似乎是最好的办法。谢谢。是的,这是一个解决方案,UpTheCreek说了同样的话。谢谢你的回答,所有的孩子们!这很有帮助,谢谢!我发布了改进版,支持ValidateAntiForgeryToken并重定向到MVC区域。