Как перенаправить на динамический URL-адрес входа в систему ASP.NET MVC

Я создаю многофункциональный веб-сайт, на котором размещаются страницы для клиентов. Первый сегмент URL-адреса будет строкой, которая идентифицирует клиента, определенного в Global.asax, используя следующую схему маршрутизации URL:

"{client}/{controller}/{action}/{id}"

это отлично работает с URL-адресами, такими как /foo/Home/Index.

однако при использовании атрибута [Authorize] я хочу перенаправить на страницу входа, которая также использует ту же схему сопоставления. Поэтому, если клиент foo, страница входа будет /foo/Account / Login вместо фиксированного/Account / Login перенаправления, определенного в интернете.конфиг.

MVC использует HttpUnauthorizedResult для возврата неавторизованного статуса 401, который, я полагаю, вызывает ASP.NET перенаправление на страницу, определенную в web.конфиг.

Так кто-нибудь знает, как переопределить ASP.NET поведение перенаправления входа в систему? Или было бы лучше перенаправить в MVC, создав пользовательский атрибут авторизации?

EDIT-ответ: после некоторые копаясь в источнике .Net, я решил, что пользовательский атрибут аутентификации является лучшим решением:

public class ClientAuthorizeAttribute: AuthorizeAttribute
{
    public override void OnAuthorization( AuthorizationContext filterContext )
    {
        base.OnAuthorization( filterContext );

        if (filterContext.Cancel && filterContext.Result is HttpUnauthorizedResult )
        {
            filterContext.Result = new RedirectToRouteResult(
                new RouteValueDictionary
                {
                    { "client", filterContext.RouteData.Values[ "client" ] },
                    { "controller", "Account" },
                    { "action", "Login" },
                    { "ReturnUrl", filterContext.HttpContext.Request.RawUrl }
                });
        }
    }
}

3 ответов


Я думаю, что основная проблема заключается в том, что если вы собираетесь piggyback на встроенном ASP.NET FormsAuthentication class (и нет никаких веских причин, по которым вы не должны), что-то в конце дня собирается позвонить FormsAuthentication.RedirectToLoginPage() который будет смотреть на один настроенный URL. Есть только один URL-адрес входа, когда-либо, и именно так они его разработали.

мой удар по проблеме (возможно, реализация Rube Goldberg) состоял бы в том, чтобы позволить ей перенаправить на одну страницу входа в root, разделяемую все клиенты, скажем / account / login. Эта страница входа на самом деле ничего не отображает; она проверяет либо параметр ReturnUrl, либо некоторое значение, которое я получил в сеансе, либо файл cookie, который идентифицирует клиента и использует его для немедленного перенаправления 302 на конкретную страницу /client/account/login. Это дополнительное перенаправление, но, вероятно, не заметно, и оно позволяет использовать встроенные механизмы перенаправления.

другой вариант-создать свой собственный пользовательский атрибут, как вы описываете и избегайте всего, что вызывает RedirectToLoginPage() метод FormsAuthentication class, так как вы будете заменять его своей собственной логикой перенаправления. (Вы можете создать свой собственный класс, который похож.) Поскольку это статический класс, я не знаю никакого механизма, с помощью которого вы могли бы просто ввести свой собственный альтернативный интерфейс и заставить его волшебно работать с существующим атрибутом [Authorize], который дует, но люди делали подобные вещи перед.

надеюсь, что это поможет!


в версии RTM ASP.NET MVC, свойство Cancel отсутствует. Этот код работает с ASP.NET MVC RTM:

using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Resources;

namespace ePegasus.Web.ActionFilters
{
    public class CustomAuthorize : AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            base.OnAuthorization(filterContext);
            if (filterContext.Result is HttpUnauthorizedResult)
            {
                filterContext.Result = new RedirectToRouteResult(
                    new System.Web.Routing.RouteValueDictionary
                        {
                                { "langCode", filterContext.RouteData.Values[ "langCode" ] },
                                { "controller", "Account" },
                                { "action", "Login" },
                                { "ReturnUrl", filterContext.HttpContext.Request.RawUrl }
                        });
            }
        }
    }
}

Edit: вы можете отключить проверку подлинности форм по умолчанию loginUrl в интернете.config-в случае, если кто-то забывает, что у вас есть пользовательский атрибут и использует встроенный атрибут [Authorize] по ошибке.

измените значение в web.config:

 <forms loginUrl="~/Account/ERROR" timeout="2880" />

затем сделайте метод действия "ошибка", который регистрирует ошибку и перенаправляет пользователя на самую общую страницу входа.


мое решение этой проблемы было пользовательским ActionResult класс:

    sealed public class RequiresLoginResult : ActionResult
    {
        override public void ExecuteResult (ControllerContext context)
        {
            var response = context.HttpContext.Response;

            var url = FormsAuthentication.LoginUrl;
            if (!string.IsNullOrWhiteSpace (url))
                url += "?returnUrl=" + HttpUtility.UrlEncode (ReturnUrl);

            response.Clear ();
            response.StatusCode = 302;
            response.RedirectLocation = url;
        }

        public RequiresLoginResult (string returnUrl = null)
        {
            ReturnUrl = returnUrl;
        }

        string ReturnUrl { get; set; }
    }