Перенаправление после входа в систему приводит к ошибке 404, если пользователь отправляет форму в действие только для POST и время его аутентификации истекло - PullRequest
5 голосов
/ 15 марта 2011

У меня есть приложение MVC, использующее проверку подлинности с помощью форм, и я получаю 404 ошибки.Происходит то, что пользователь отправляет форму в действие только для POST, когда время аутентификации истекло, и они перенаправляются на страницу входа.После входа они перенаправляются обратно на исходный URL с помощью GET, что приведет к ошибке 404, так как действие только для POST.

У меня есть два вопроса:

  1. Моя идея обойти это - каким-то образом определить, является ли перенаправляемое действие действием только для POST, и вместо этого перенаправить на домашнюю страницу.Как бы я поступил так?

  2. В идеале приложение запомнит опубликованные значения и отправит их на исходный URL через POST, но я понятия не имею, как обойти формыАутентификация для этого, и я подозреваю, что это может привести к уязвимостям безопасности.Это хорошая идея, и если да, то как это можно сделать?

Ответы [ 5 ]

3 голосов
/ 16 марта 2011

Я создал фильтр действий в результате ответов выше, я оставлю его здесь для потомков. Он передает все указанные параметры от предпринятого действия до действия перенаправления.

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)
{
    ...
}
3 голосов
/ 15 марта 2011

Простым исправлением будет создание действия GET only с тем же именем, что и у POST only, которое перенаправляет на домашнюю страницу. Создание решения, которое возобновит публикацию формы после входа в систему, потребовало бы значительных усилий при минимальной выгоде.

UPDATE:

Что касается объема работы, то было бы создать все эти действия GET. Более элегантным вариантом было бы создание атрибута специально для этого сценария, такого как HttpPostOrRedirectAttribute, который вы могли бы использовать для украшения этих постов. только действия, а не использовать HttpPostAttribute. Поведение таково, что он принимает сообщения, но вместо броска 404 выполняет перенаправления для других глаголов.

1 голос
/ 19 октября 2017

Вот улучшенная версия ответа @stusherwin с поддержкой ValidateAntiForgeryToken и MVC областей .

Вероятно, ваши действия 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)
{
    ...
}
1 голос
/ 15 марта 2011

Как насчет создания действия GET, которое перенаправляет на страницу, содержащую оригинальную форму?

Вы можете добавить некоторую дополнительную информацию к данным ModelView, чтобы указать, что сообщение должно отображаться для пользователя. В этом сообщении должно быть что-то вроде этого ...

"Вы отправили эту форму, когда вышли из системы - теперь, когда вы вошли в систему, хотите ли вы продолжить отправку"

Или, если вы действительно хотите, дополнительная информация в ModelViewData может привести к автоматической отправке формы.

Как вы говорите, это довольно сложная работа, если у вас много страниц форм, но должен быть какой-то способ инкапсулировать поведение для повторного использования.

1 голос
/ 15 марта 2011

Столкнувшись именно с этой проблемой, мы создаем действия [HttpGet] для Сообщений, которые перенаправляются в Индекс; пользователь теряет введенные данные, так что это не так уж и здорово, но это был быстрый путь для нас.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...