Nicht autorisierte Controller in ASP.NET MVC redirect

Ich habe einen Controller in ASP.NET MVC, den ich auf die Admin-Rolle beschränkt habe:

[Authorize(Roles = "Admin")] public class TestController : Controller { ... 

Wenn ein Benutzer, der nicht in der Admin-Rolle ist, zu diesem Controller navigiert, wird er mit einem leeren Bildschirm begrüßt.

Was ich tun möchte, ist, sie zu “View” umzuleiten, die besagt “Sie müssen in der Rolle des Administrators sein, um auf diese Ressource zugreifen zu können”.

Eine Möglichkeit, dies zu tun, an die ich gedacht habe, besteht darin, eine Überprüfung in jeder Aktionsmethode für IsUserInRole () durchzuführen und, wenn sie nicht in der Rolle ist, diese Informationsansicht zurückzugeben. Allerdings müsste ich das in jede Aktion schreiben, die das DRY-Prinzip durchbricht und offensichtlich schwer zu verwalten ist.

Erstellen Sie ein benutzerdefiniertes Autorisierungsattribut basierend auf AuthorizeAttribute und überschreiben Sie OnAuthorization, um die Überprüfung durchzuführen, wie Sie es ausführen möchten. Normalerweise setzt AuthorizeAttribute das Filterergebnis auf HttpUnauthorizedResult, wenn die Berechtigungsprüfung fehlschlägt. Sie könnten es stattdessen auf ein ViewResult (Ihrer Fehleransicht) setzen lassen.

EDIT : Ich habe ein paar Blog-Beiträge, die im Detail gehen:

Beispiel:

  [AttributeUsage( AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false )] public class MasterEventAuthorizationAttribute : AuthorizeAttribute { ///  /// 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. ///  public virtual string MasterName { get; set; } ///  /// The name of the view to render on authorization failure. Default is "Error". ///  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 */); } } 

Sie können mit dem überschreibbaren HandleUnauthorizedRequest in Ihrem benutzerdefinierten AuthorizeAttribute

So was:

 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" } }); } 

Sie können auch so etwas tun:

 private class RedirectController : Controller { public ActionResult RedirectToSomewhere() { return RedirectToAction("Action", "Controller"); } } 

Jetzt können Sie es in Ihrer HandleUnauthorizedRequest Methode auf diese Weise verwenden:

 filterContext.Result = (new RedirectController()).RedirectToSomewhere(); 

Der Code von “tvanfosson” gab mir “Fehler beim Ausführen von Child Request”. Ich habe die OnAuthorization wie folgt geändert:

 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"); } } 

Das funktioniert gut und ich zeige die TempData auf der Fehlerseite. Danke an “tvanfosson” für das Code-Snippet. Ich verwende Windows-Authentifizierung und _isAuthorized ist nichts anderes als HttpContext.User.Identity.IsAuthenticated …

Ich hatte das gleiche Problem. Anstatt den MVC-Code herauszufinden, entschied ich mich für einen billigen Hack, der zu funktionieren scheint. In meiner Global.asax-class:

 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) 

Dieses Problem hat mich seit einigen Tagen verfolgt, und als ich die Antwort gefunden habe, die mit Tvanfossons Antwort oben funktioniert, dachte ich, es würde sich lohnen, den coreteil der Antwort zu betonen und einige damit zusammenhängende Fragen anzusprechen.

Die coreantwort ist dies, süß und einfach:

 filterContext.Result = new HttpUnauthorizedResult(); 

In meinem Fall erben wir von einem Basis-Controller, also überschreibe ich in jedem Controller, der von ihm erbt, OnAuthorize:

 protected override void OnAuthorization(AuthorizationContext filterContext) { base.OnAuthorization(filterContext); YourAuth(filterContext); // do your own authorization logic here } 

Das Problem war, dass ich in ‘YourAuth’ zwei Dinge ausprobierte, von denen ich dachte, dass sie nicht nur funktionieren würden, sondern auch die Anfrage sofort beenden würden. Nun, so funktioniert es nicht. Also zuerst, die zwei Dinge, die nicht funktionieren, unerwartet:

 filterContext.RequestContext.HttpContext.Response.Redirect("/Login"); // doesn't work! FormsAuthentication.RedirectToLoginPage(); // doesn't work! 

Nicht nur, dass diese nicht funktionieren, sie beenden die Anfrage auch nicht. Was bedeutet Folgendes:

 if (!success) { filterContext.Result = new HttpUnauthorizedResult(); } DoMoreStuffNowThatYouThinkYourAuthorized(); 

Nun, selbst mit der richtigen Antwort oben, der Fluss der Logik geht weiter! Du wirst DoMoreStuff … innerhalb von OnAuthorize treffen. Behalte das im Hinterkopf (DoMore … sollte also in einem anderen sein).

Aber mit der richtigen Antwort, während OnAuthorize Fluss der Logik weiter bis zum Ende immer noch, danach erhalten Sie wirklich, was Sie erwarten: eine Weiterleitung auf Ihre Anmeldeseite (wenn Sie eine in Forms Auth in Ihrer Webconfig gesetzt haben).

Aber unerwartet, 1) Response.Redirect (“/ Login”) funktioniert nicht: die Aktion-Methode wird immer noch aufgerufen, und 2) FormsAuthentication.RedirectToLoginPage (); macht dasselbe: die Action-Methode wird immer noch aufgerufen!

Das scheint mir total falsch zu sein, besonders mit letzterem: Wer hätte gedacht, dass FormsAuthentication.RedirectToLoginPage die Anfrage nicht beendet, oder ob das Äquivalent von filterContext.Result = new HttpUnauthorizedResult () funktioniert?

Sie sollten Ihr eigenes Autorisierungsfilter-Attribut erstellen.

Hier ist meins zu lernen;)

 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 

Ich hätte das als Kommentar hinterlassen, aber ich brauche mehr Wiederholungen, jedenfalls wollte ich Nicholas Peterson nur sagen, dass vielleicht das zweite Argument an den Redirect-Aufruf weitergegeben wurde, um ihm zu sagen, dass er die Antwort hätte beenden müssen. Nicht die eleganteste Art, damit umzugehen, aber es funktioniert tatsächlich.

Damit

 filterContext.RequestContext.HttpContext.Response.Redirect("/Login", true); 

Anstatt von

 filterContext.RequestContext.HttpContext.Response.Redirect("/Login); 

Du hättest das in deinem Controller:

  protected override void OnAuthorization(AuthorizationContext filterContext) { if(!User.IsInRole("Admin") { base.OnAuthorization(filterContext); filterContext.RequestContext.HttpContext.Response.Redirect("/Login", true); } } 

Vielleicht erhalten Sie eine leere Seite, wenn Sie von Visual Studio unter Entwicklungsserver mit Windows-Authentifizierung ausführen ( vorheriges Thema ).

Wenn Sie eine Bereitstellung nach IIS durchführen, können Sie benutzerdefinierte Fehlerseiten für bestimmte Statuscodes konfigurieren, in diesem Fall 401. Fügen Sie httpErrors unter system.webServer hinzu:

     

Erstellen Sie anschließend die ErrorController.Unauthorized-Methode und die entsprechende benutzerdefinierte Ansicht.

Fügen Sie in Ihrer Datei Startup.Auth.cs diese Zeile hinzu:

 LoginPath = new PathString("/Account/Login"), 

Beispiel:

 // 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( validateInterval: TimeSpan.FromMinutes(30), regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager)) } });