Как перенаправить на динамический URL-адрес входа в ASP.NET MVC
-
21-08-2019 - |
Вопрос
Я создаю многопользовательский веб-сайт, на котором размещаются страницы для клиентов.Первый сегмент URL-адреса будет строкой, идентифицирующей клиента, определенного в Global.asax с использованием следующей схемы маршрутизации URL-адресов:
"{client}/{controller}/{action}/{id}"
Это отлично работает с такими URL-адресами, как /foo/Home/Index.
Однако при использовании атрибута [Authorize] я хочу перенаправить на страницу входа, которая также использует ту же схему сопоставления.Таким образом, если клиент — foo, страница входа будет /foo/Account/Login вместо фиксированного перенаправления /Account/Login, определенного в web.config.
MVC использует HttpUnauthorizedResult для возврата неавторизованного статуса 401, который, как я предполагаю, заставляет ASP.NET перенаправляться на страницу, определенную в web.config.
Итак, кто-нибудь знает, как переопределить поведение перенаправления входа в систему ASP.NET?Или было бы лучше перенаправить в MVC, создав собственный атрибут авторизации?
РЕДАКТИРОВАТЬ - Ответ: после некоторого копания в источнике .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 }
});
}
}
}
Решение
Я думаю, что основная проблема заключается в том, что если вы собираетесь использовать встроенный класс ASP.NET FormsAuthentication (и нет веской причины, по которой вы не должны этого делать), что-то в конце дня вызовет FormsAuthentication.RedirectToLoginPage()
который будет просматривать один настроенный URL-адрес.Всегда существует только один URL-адрес для входа, и именно так они его и задумали.
Мой вариант решения проблемы (возможно, реализация Руба Голдберга) заключался бы в том, чтобы позволить ей перенаправляться на одну страницу входа в корне, общую для всех клиентов, скажем, /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 }
});
}
}
}
}
Редактировать: Возможно, вы захотите отключить логинUrl аутентификации форм по умолчанию в web.config — на случай, если кто-то забудет, что у вас есть собственный атрибут, и по ошибке использует встроенный атрибут [Authorize].
Измените значение в web.config:
<forms loginUrl="~/Account/ERROR" timeout="2880" />
Затем создайте метод действия «ERROR», который регистрирует ошибку и перенаправляет пользователя на самую общую страницу входа, которая у вас есть.
Мое решение этой проблемы было кастомным 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; }
}
Тем не менее, если вы решите использовать встроенную аутентификацию ASP.NET FormsAuthentication, вы можете переопределить Application_AuthenticateRequest
в Global.asax.cs
следующее:
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
string url = Request.RawUrl;
if (url.Contains(("Account/Login"))
{
return;
}
if (Context.User == null)
{
// Your custom tenant-aware logic
if (url.StartsWith("/foo"))
{
// Your custom login page.
Response.Redirect("/foo/Account/Login");
Response.End();
return;
}
}
}