Перенаправление несанкционированного контроллера в ASP.NET MVC
У меня есть контроллер в ASP.NET MVC, который я ограничил ролью администратора:
[Authorize(Roles = "Admin")]
public class TestController : Controller
{
...
если пользователь, который не находится в роли администратора переходит к этому контроллеру они приветствуются с пустым экраном.
что я хотел бы сделать, это перенаправить их на просмотр, который говорит: "вам нужно быть в роли администратора, чтобы иметь доступ к этому ресурсу."
один из способов сделать это, о котором я думал, - это проверить каждый метод действия на IsUserInRole() , а если нет в роли затем верните это информационное представление. Тем не менее, я должен был бы поместить это в каждое действие, которое нарушает сухой принцип и, очевидно, громоздко поддерживать.
9 ответов:
создайте пользовательский атрибут авторизации на основе AuthorizeAttribute и переопределите OnAuthorization, чтобы выполнить проверку, как вы хотите это сделать. Обычно AuthorizeAttribute устанавливает результат фильтра в HttpUnauthorizedResult, если проверка авторизации завершается неудачно. Вместо этого вы можете установить его в ViewResult (вашего представления ошибок).
EDIT: у меня есть несколько сообщений в блоге, которые идут в более деталь:
- http://farm-fresh-code.blogspot.com/2011/03/revisiting-custom-authorization-in.html
- http://farm-fresh-code.blogspot.com/2009/11/customizing-authorization-in-aspnet-mvc.html
пример:
[AttributeUsage( AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false )] public class MasterEventAuthorizationAttribute : AuthorizeAttribute { /// <summary> /// The name of the master page or view to use when rendering the view on authorization failure. Default /// is null, indicating to use the master page of the specified view. /// </summary> public virtual string MasterName { get; set; } /// <summary> /// The name of the view to render on authorization failure. Default is "Error". /// </summary> public virtual string ViewName { get; set; } public MasterEventAuthorizationAttribute() : base() { this.ViewName = "Error"; } protected void CacheValidateHandler( HttpContext context, object data, ref HttpValidationStatus validationStatus ) { validationStatus = OnCacheAuthorization( new HttpContextWrapper( context ) ); } public override void OnAuthorization( AuthorizationContext filterContext ) { if (filterContext == null) { throw new ArgumentNullException( "filterContext" ); } if (AuthorizeCore( filterContext.HttpContext )) { SetCachePolicy( filterContext ); } else if (!filterContext.HttpContext.User.Identity.IsAuthenticated) { // auth failed, redirect to login page filterContext.Result = new HttpUnauthorizedResult(); } else if (filterContext.HttpContext.User.IsInRole( "SuperUser" )) { // is authenticated and is in the SuperUser role SetCachePolicy( filterContext ); } else { ViewDataDictionary viewData = new ViewDataDictionary(); viewData.Add( "Message", "You do not have sufficient privileges for this operation." ); filterContext.Result = new ViewResult { MasterName = this.MasterName, ViewName = this.ViewName, ViewData = viewData }; } } protected void SetCachePolicy( AuthorizationContext filterContext ) { // ** IMPORTANT ** // Since we're performing authorization at the action level, the authorization code runs // after the output caching module. In the worst case this could allow an authorized user // to cause the page to be cached, then an unauthorized user would later be served the // cached page. We work around this by telling proxies not to cache the sensitive page, // then we hook our custom authorization code into the caching mechanism so that we have // the final say on whether a page should be served from the cache. HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache; cachePolicy.SetProxyMaxAge( new TimeSpan( 0 ) ); cachePolicy.AddValidationCallback( CacheValidateHandler, null /* data */); } }
вы можете работать с overridable
HandleUnauthorizedRequestвнутри вашего пользовательскогоAuthorizeAttributeтакой:
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { // Returns HTTP 401 by default - see HttpUnauthorizedResult.cs. filterContext.Result = new RedirectToRouteResult( new RouteValueDictionary { { "action", "YourActionName" }, { "controller", "YourControllerName" }, { "parameterName", "YourParameterValue" } }); }вы также можете сделать что-то вроде этого:
private class RedirectController : Controller { public ActionResult RedirectToSomewhere() { return RedirectToAction("Action", "Controller"); } }теперь вы можете использовать ее в своем
HandleUnauthorizedRequestметод таким образом:filterContext.Result = (new RedirectController()).RedirectToSomewhere();
код "tvanfosson "давал мне"ошибка выполнения дочернего запроса".. Я изменил OnAuthorization следующим образом:
public override void OnAuthorization(AuthorizationContext filterContext) { base.OnAuthorization(filterContext); if (!_isAuthorized) { filterContext.Result = new HttpUnauthorizedResult(); } else if (filterContext.HttpContext.User.IsInRole("Administrator") || filterContext.HttpContext.User.IsInRole("User") || filterContext.HttpContext.User.IsInRole("Manager")) { // is authenticated and is in one of the roles SetCachePolicy(filterContext); } else { filterContext.Controller.TempData.Add("RedirectReason", "You are not authorized to access this page."); filterContext.Result = new RedirectResult("~/Error"); } }Это хорошо работает, и я показываю TempData на странице ошибки. Спасибо "tvanfosson"за фрагмент кода. Я использую проверку подлинности windows и _isAuthorized-это не что иное, как HttpContext.Пользователь.Тождественность.Метод isauthenticated...
У меня была та же проблема. Вместо того, чтобы выяснить код MVC, я выбрал дешевый хак, который, кажется, работает. В моем глобальном.класс эйсакс:
member x.Application_EndRequest() = if x.Response.StatusCode = 401 then let redir = "?redirectUrl=" + Uri.EscapeDataString x.Request.Url.PathAndQuery if x.Request.Url.LocalPath.ToLowerInvariant().Contains("admin") then x.Response.Redirect("/Login/Admin/" + redir) else x.Response.Redirect("/Login/Login/" + redir)
эта проблема преследовала меня уже несколько дней, поэтому, найдя ответ, который утвердительно работает с ответом tvanfosson выше, я подумал, что было бы целесообразно подчеркнуть основную часть ответа и обратиться к некоторым связанным с ним уловам ya.
основной ответ таков, сладкий и простой:
filterContext.Result = new HttpUnauthorizedResult();в моем случае я наследую от базового контроллера, поэтому в каждом контроллере, который наследует от него, я переопределяю OnAuthorize:
protected override void OnAuthorization(AuthorizationContext filterContext) { base.OnAuthorization(filterContext); YourAuth(filterContext); // do your own authorization logic here }проблема в том, что в "YourAuth" я попробовал две вещи, которые, как я думал, не только сработают, но и немедленно прекратят запрос. Ну, это не так работает. Итак, во-первых, две вещи, которые не работают, неожиданно:
filterContext.RequestContext.HttpContext.Response.Redirect("/Login"); // doesn't work! FormsAuthentication.RedirectToLoginPage(); // doesn't work!они не только не работают,но и не заканчивают запрос. Что означает следующее:
if (!success) { filterContext.Result = new HttpUnauthorizedResult(); } DoMoreStuffNowThatYouThinkYourAuthorized();Ну, даже с правильным ответом выше, поток логики все еще продолжается! Вы все равно попадете в DoMoreStuff... внутри OnAuthorize. Так что в виду (делать гораздо... должно быть в другом месте поэтому).
но с правильным ответом, в то время как onauthorize поток логики продолжается до конца еще, после этого вы действительно получите то, что вы ожидаете: перенаправление на страницу входа в систему (если у вас есть один набор в формах auth в вашем webconfig).
, но неожиданно, 1) Ответ.Redirect ("/Login") не работает: метод действия по-прежнему вызывается, и 2) FormsAuthentication.RedirectToLoginPage(); делает то же самое: действие метод все равно вызывается!
, который кажется мне совершенно неправильным, особенно с последнего: Кто бы мог подумать, что метод formsauthentication.RedirectToLoginPage не завершает запрос или делает эквивалентное выше того, что filterContext.Result = new HttpUnauthorizedResult () делает?
вы должны создать свой собственный атрибут Authorize-filter.
вот мой для изучения ;)
Public Class RequiresRoleAttribute : Inherits ActionFilterAttribute Private _role As String Public Property Role() As String Get Return Me._role End Get Set(ByVal value As String) Me._role = value End Set End Property Public Overrides Sub OnActionExecuting(ByVal filterContext As System.Web.Mvc.ActionExecutingContext) If Not String.IsNullOrEmpty(Me.Role) Then If Not filterContext.HttpContext.User.Identity.IsAuthenticated Then Dim redirectOnSuccess As String = filterContext.HttpContext.Request.Url.AbsolutePath Dim redirectUrl As String = String.Format("?ReturnUrl={0}", redirectOnSuccess) Dim loginUrl As String = FormsAuthentication.LoginUrl + redirectUrl filterContext.HttpContext.Response.Redirect(loginUrl, True) Else Dim hasAccess As Boolean = filterContext.HttpContext.User.IsInRole(Me.Role) If Not hasAccess Then Throw New UnauthorizedAccessException("You don't have access to this page. Only " & Me.Role & " can view this page.") End If End If Else Throw New InvalidOperationException("No Role Specified") End If End Sub End Class
оставил бы это как комментарий, но мне нужно больше репутации, в любом случае я просто хотел упомянуть Николасу Петерсону, что, возможно, передача второго аргумента вызову перенаправления, чтобы сказать ему, чтобы закончить ответ, сработала бы. Не самый изящный способ справиться с этим, но это действительно работает.
Так
filterContext.RequestContext.HttpContext.Response.Redirect("/Login", true);вместо
filterContext.RequestContext.HttpContext.Response.Redirect("/Login);Так что у вас будет это в вашем контроллере:
protected override void OnAuthorization(AuthorizationContext filterContext) { if(!User.IsInRole("Admin") { base.OnAuthorization(filterContext); filterContext.RequestContext.HttpContext.Response.Redirect("/Login", true); } }
возможно, вы получаете пустую страницу при запуске из Visual Studio под сервером разработки с использованием проверки подлинности Windows (предыдущей теме).
при развертывании в IIS можно настроить пользовательские страницы ошибок для определенных кодов состояния, в данном случае 401. Добавьте httpErrors в систему.веб-сервер:
<httpErrors> <remove statusCode="401" /> <error statusCode="401" path="/yourapp/error/unauthorized" responseMode="Redirect" /> </httpErrors>затем создайте ErrorController.Несанкционированный метод и соответствующий пользовательский вид.
в свой стартап.Автор.cs файл добавьте эту строку:
LoginPath = new PathString("/Account/Login"),пример:
// Enable the application to use a cookie to store information for the signed in user // and to use a cookie to temporarily store information about a user logging in with a third party login provider // Configure the sign in cookie app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = new PathString("/Account/Login"), Provider = new CookieAuthenticationProvider { // Enables the application to validate the security stamp when the user logs in. // This is a security feature which is used when you change a password or add an external login to your account. OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>( validateInterval: TimeSpan.FromMinutes(30), regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager)) } });
Comments