Redirection d'un contrôleur non autorisé dans ASP.NET MVC
j'ai un contrôleur en ASP.NET MVC que j'ai limité au rôle d'administrateur:
[Authorize(Roles = "Admin")]
public class TestController : Controller
{
...
Si un utilisateur qui n'est pas dans le rôle d'Administrateur accède à ce contrôleur, ils sont accueillis avec un écran vide.
Ce que je voudrais faire est de les rediriger vers la Vue qui dit "vous devez être dans le rôle d'Administrateur pour pouvoir accéder à cette ressource."
une façon de faire cela à laquelle j'ai pensé est d'avoir un contrôle dans chaque action méthode sur IsUserInRole() et si ce n'est dans son rôle de renvoyer cette information vue. Cependant, je devrais mettre que dans chaque Action qui casse le principal sec et est évidemment lourd à entretenir.
9 réponses
créer un attribut d'autorisation personnalisé basé sur AuthorizeAttribute et outrepasser OnAuthorization pour effectuer la vérification comment vous voulez qu'il soit fait. Normalement, AuthorizeAttribute définira le résultat du filtre à HttpUnauthorizedResult si la vérification d'autorisation échoue. Vous pouvez le faire définir à un ViewResult (de votre vue D'erreur) à la place.
EDIT : j'ai un couple de billets de blog qui vont dans plus de détails:
- 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
exemple:
[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 */);
}
}
vous pouvez travailler avec la surimprimable HandleUnauthorizedRequest
à l'intérieur de votre coutume AuthorizeAttribute
comme ceci:
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" }
});
}
vous pouvez aussi faire quelque chose comme ceci:
private class RedirectController : Controller
{
public ActionResult RedirectToSomewhere()
{
return RedirectToAction("Action", "Controller");
}
}
Maintenant vous pouvez l'utiliser dans votre méthode HandleUnauthorizedRequest
de cette façon:
filterContext.Result = (new RedirectController()).RedirectToSomewhere();
le code de "tvanfosson "me donnait"erreur d'exécution D'une requête enfant".. J'ai changé L'OnAuthorization comme ceci:
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");
}
}
cela fonctionne bien et je montre le TempData sur la page d'erreur. Merci à "tvanfosson" pour le code. J'utilise l'authentification windows et _isAuthorized n'est rien d'autre que HttpContext.Utilisateur.Identité.IsAuthenticated...
j'ai eu le même problème. Plutôt que de comprendre le code MVC, j'ai opté pour un piratage bon marché qui semble fonctionner. Dans mon Global.classe asax:
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)
ce problème me hante depuis quelques jours maintenant, donc en trouvant la réponse qui fonctionne avec la réponse de tvanfosson ci-dessus, j'ai pensé qu'il serait utile de mettre l'accent sur la partie centrale de la réponse, et d'adresser quelques attrapes liées ya.
la réponse principale est ceci, doux et simple:
filterContext.Result = new HttpUnauthorizedResult();
dans mon cas, j'hérite d'un contrôleur de base, donc dans chaque contrôleur qui en hérite, j'ai la priorité sur Onauthorized:
protected override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
YourAuth(filterContext); // do your own authorization logic here
}
le problème était que dans 'Yourrauth', j'ai essayé deux choses que je pensais ne fonctionnerait pas seulement, mais aussi immédiatement mettre fin à la demande. Eh bien, ce n'est pas comment il fonctionne. Tout d'abord, les deux choses qui ne fonctionnent pas, de façon inattendue:
filterContext.RequestContext.HttpContext.Response.Redirect("/Login"); // doesn't work!
FormsAuthentication.RedirectToLoginPage(); // doesn't work!
non seulement ceux qui ne fonctionnent pas, ils ne mettent pas fin à la demande non plus. Qui signifie ce qui suit:
if (!success) {
filterContext.Result = new HttpUnauthorizedResult();
}
DoMoreStuffNowThatYouThinkYourAuthorized();
eh Bien, même avec la bonne réponse ci-dessus, le flux de logique encore continue! Tu frapperas encore DoMoreStuff... au sein de Onauthorized. Alors gardez cela à l'esprit (DoMore... devrait être dans un autre donc).
mais avec la bonne réponse, alors que le flux Onauthorizate de logique continue jusqu'à la fin encore, après que vous obtenez vraiment ce que vous attendez: une redirection vers votre page de connexion (si vous avez un jeu dans les formulaires auth dans votre webconfig).
mais de façon inattendue, 1) réponse.Redirect ("/Login") ne fonctionne pas: la méthode D'Action fonctionne toujours est appelée, et 2) FormsAuthentication.RedirectToLoginPage (); fait la même chose: la méthode D'Action est toujours appelée!
ce qui me semble totalement erroné, surtout avec ce dernier: qui aurait pensé que formait L'authentification.RedirectToLoginPage ne termine pas la requête, ou faire l'équivalent ci-dessus de ce filterContext.Résultat = new HttpUnauthorizedResult ()?
vous devez créer votre propre attribut Authorize-filter.
, Voici la mine à l'étude ;)
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
aurait laissé ceci comme un commentaire mais j'ai besoin de plus de rep, de toute façon je voulais juste mentionner à Nicholas Peterson que peut-être passer le deuxième argument à L'appel de redirection pour lui dire de mettre fin à la réponse aurait fonctionné. Pas la plus élégante façon de gérer cela, mais il fait le travail.
Donc
filterContext.RequestContext.HttpContext.Response.Redirect("/Login", true);
au lieu de
filterContext.RequestContext.HttpContext.Response.Redirect("/Login);
donc vous avez ceci dans votre contrôleur:
protected override void OnAuthorization(AuthorizationContext filterContext)
{
if(!User.IsInRole("Admin")
{
base.OnAuthorization(filterContext);
filterContext.RequestContext.HttpContext.Response.Redirect("/Login", true);
}
}
peut-être que vous obtenez une page blanche lorsque vous exécutez à partir de Visual Studio sous le serveur de développement en utilisant l'authentification Windows ( sujet précédent ).
si vous utilisez IIS, vous pouvez configurer des pages d'erreur personnalisées pour des codes d'état spécifiques, dans ce cas 401. Ajouter httpErrors sous système.webServer:
<httpErrors>
<remove statusCode="401" />
<error statusCode="401" path="/yourapp/error/unauthorized" responseMode="Redirect" />
</httpErrors>
crée alors ErrorController.Méthode non autorisée et vue personnalisée correspondante.
dans votre Startup.Auth.ajouter cette ligne:
LoginPath = new PathString("/Account/Login"),
exemple:
// 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))
}
});