2009-09-30 3 views
43

Dans mon application ASP.NET MVC, j'ai la plupart des contrôleurs décorées avecASP.NET MVC - Comment afficher les erreurs non autorisées sur la page de connexion?

[Authorize(Roles="SomeGroup")] 

Lorsqu'un utilisateur n'est pas autorisé à accéder à quelque chose, ils sont envoyés à « ~/Connexion » qui est l'action de connexion sur mon compte manette.

Comment puis-je déterminer qu'un utilisateur a atteint la page de connexion parce qu'il n'est pas autorisé à afficher une erreur appropriée?

Répondre

28

Vous pouvez rechercher la valeur de la chaîne de requête ?ReturnUrl=, ou vous pouvez créer votre propre filtre d'autorisation & définir un champ dans TempData indiquant la raison.

Voici un simple filtre personnalisé qui fera l'affaire:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 
public class CustomAuthorizeAttribute : AuthorizeAttribute 
{ 

    // NOTE: This is not thread safe, it is much better to store this 
    // value in HttpContext.Items. See Ben Cull's answer below for an example. 
    private bool _isAuthorized; 

    protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext) 
    { 
     _isAuthorized = base.AuthorizeCore(httpContext); 
     return _isAuthorized; 
    } 

    public override void OnAuthorization(AuthorizationContext filterContext) 
    { 
     base.OnAuthorization(filterContext); 

     if(!_isAuthorized) 
     { 
      filterContext.Controller.TempData.Add("RedirectReason", "Unauthorized"); 
     } 
    } 
} 

Alors à votre avis, vous pouvez faire quelque chose comme ceci:

@if(TempData["RedirectReason"] == "Unauthorized") 
{ 
    <b>You don't have permission to access that area</b> 
} 

(Bien que je vous recommande un meilleure approche que ces cordes magiques, mais vous obtenez le point)

+0

Facile à mettre en œuvre et cela fonctionne. Merci. –

+0

Merci Ben, je mets en place la même chose de mon côté et fonctionne très bien! –

+0

Cette réponse est acceptée, mais elle n'est pas sûre pour les threads, veuillez voir la réponse ci-dessous pour plus de détails. Veuillez mettre à jour votre code pour qu'il soit sans danger pour les threads, beaucoup de développeurs peuvent ne pas lire d'autres réponses et utiliser le vôtre comme le meilleur. –

74

MISE À JOUR (juin 2015): @ daniel-lidström a correctement signalé que vous ne devriez pas utiliser Response.Redirect dans une application ASP.NET MVC. Pour plus d'informations sur pourquoi, s'il vous plaît voir ce lien: Response.Redirect and ASP.NET MVC – Do Not Mix.

MISE À JOUR (septembre 2014): Je ne sais pas quand HandleUnauthorizedRequest a été ajouté à la AuthorizeAttribute, mais de toute façon j'ai été en mesure de préciser le code AuthorizeRedirect en quelque chose de plus petit et plus simple.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 
public class AuthorizeRedirect : AuthorizeAttribute 
{ 
    public string RedirectUrl = "~/Error/Unauthorized"; 

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
    { 
     base.HandleUnauthorizedRequest(filterContext); 

     if (filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated) 
     { 
      filterContext.Result = new RedirectResult(RedirectUrl); 
     } 
    } 
} 

Réponse originale ci-dessous (encore pleinement fonctionnel)

J'ai laissé cette réponse ici car il vous donne encore un aperçu de la façon dont les travaux de pipeline d'autorisation.

Pour tous ceux qui atterrissent ici, j'ai modifié la réponse de Ben Scheirman pour rediriger automatiquement vers une page non autorisée lorsque l'utilisateur est connecté mais pas autorisé. Vous pouvez modifier le chemin de redirection à l'aide du paramètre de nom RedirectUrl.

EDIT: J'ai fait le thread-safe solution grâce aux conseils de Tarynn et MSDN

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 
public class AuthorizeRedirect : AuthorizeAttribute 
{ 
    private const string IS_AUTHORIZED = "isAuthorized"; 

    public string RedirectUrl = "~/error/unauthorized"; 

    protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext) 
    { 
     bool isAuthorized = base.AuthorizeCore(httpContext); 

     httpContext.Items.Add(IS_AUTHORIZED, isAuthorized); 

     return isAuthorized; 
    } 

    public override void OnAuthorization(AuthorizationContext filterContext) 
    { 
     base.OnAuthorization(filterContext); 

     var isAuthorized = filterContext.HttpContext.Items[IS_AUTHORIZED] != null 
      ? Convert.ToBoolean(filterContext.HttpContext.Items[IS_AUTHORIZED]) 
      : false; 

     if (!isAuthorized && filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated) 
     { 
      filterContext.RequestContext.HttpContext.Response.Redirect(RedirectUrl); 
     } 
    } 
} 
+5

De MSDN: provenant de AuthorizeAttribute Si vous dérivez de la classe AuthorizeAttribute, le type dérivé doit être thread-safe. Par conséquent, ne stockez pas l'état dans une instance du type lui-même (par exemple, dans un champ d'instance) sauf si cet état est destiné à s'appliquer à toutes les demandes. Au lieu de cela, stockez l'état par requête dans la propriété Items, accessible via les objets de contexte transmis à AuthorizeAttribute. n'est pas le _isAutorisé un champ d'instance? – Tarynn

+0

Wow bonne prise, je vais mettre à jour la solution ci-dessus pour correspondre. –

+0

Est-ce que cette méthode fonctionne avec FormsAuth et la redirection par défaut? –

4

Si vous avez un contrôleur et ne veulent pas avoir une URL en vous vous codez peut rediriger de cette façon aussi. Il ne changera pas l'URL dans la barre d'adresse du navigateur afin que l'utilisateur ne verra jamais l'URL de la page non autorisée. Cela a été écrit en MVC 3. Cette méthode fonctionnera également si vous voulez les rediriger vers une page de connexion ou si vous voulez les rediriger vers une page pour simplement leur dire qu'ils ne sont pas autorisés. J'avais une section dans le programme que certains utilisateurs n'avaient pas les droits, mais ils étaient connectés, c'est ce que j'ai utilisé.

public class AuthorizedRedirect : AuthorizeAttribute 
{ 
    protected override bool AuthorizeCore(HttpContextBase httpContext) 
    { 
     bool isAuthorized = base.AuthorizeCore(httpContext); 
     return isAuthorized; 
    } 
protect override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
{ 
    filterContext.RequestContext.RouteData.Values["controller"] = "error"; 
    filterContext.Result = new ViewResult { ViewName = "unauthorized" }; 
} 
+0

Comme autre variante pour afficher le message dans une vue partagée, par ex. 'var vr = new ViewResult(); vr.ViewName = "Info"; vr.ViewBag.Message = "Vous n'êtes pas autorisé pour cette page, contactez-nous."; filterContext.Result = vr; ' – subsci

2

Et une version encore plus simple qui utilise les paramètres FormsAuthentication. Pour ceux qui ne sont pas familiers avec Contract, Contract.Requires est un ajout .NET 4. Avantages et inconvénients d'utiliser Code Contracts.

public class RequiresAttribute : AuthorizeAttribute 
{ 
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
    { 
     Contract.Requires(filterContext != null); 

     HttpContextBase context = filterContext.RequestContext.HttpContext; 

     if (context.User.Identity.IsAuthenticated) 
     { 
      // user does not possess the required role permission 
      string url = context.GetCustomErrorUrl(401); 
      context.Response.Redirect(url); 
     } 
     else 
     { 

      // redirect the user to the login page 
      string extraQueryString = context.Request.RawUrl; 
      FormsAuthentication.RedirectToLoginPage(extraQueryString); 
     } 
    } 
} 
1

Allant plus loin de la réponse de divide_byzero même si vous ne disposez pas d'un contrôleur, vous pouvez toujours utiliser le HandleUnauthorizedRequest pour changer la redirection.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 
    public class AuthoriseRedirect : AuthorizeAttribute 
    { 
     protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
     { 
      filterContext.RequestContext.HttpContext.Response.Redirect("UrlToRedirectTo"); 
     } 
    } 

est très pratique si vous avez un héritage site webforms que vous convertira à MVC sur une période de temps plus longue .....!

4

La méthode de Ben Cull fonctionne bien, mais souvenez-vous qu'il existe deux classes AuthorizeAttribute: une dans System.Web.HTTP (utilisée par l'API Web) et l'autre dans System.Web.Mvc. La méthode de Ben utilise la classe System.Web.Mvc. Pour plus de clarté, je suggère d'utiliser le chemin complet.

Si vous utilisez l'API Web aux côtés de MVC, vous devrez implémenter deux filtres:

public class AuthorizeRedirectMVCAttribute : System.Web.Mvc.AuthorizeAttribute 
{ 
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
    { 
     base.HandleUnauthorizedRequest(filterContext); 

     if (filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated) 
     { 
      filterContext.Result = new RedirectResult("~/Account/AccessDenied"); 
     } 
    } 
} 

public class AuthorizeRedirectAPIAttribute : System.Web.Http.AuthorizeAttribute 
{ 
    protected override void HandleUnauthorizedRequest(HttpActionContext actionContext) 
    { 
     base.HandleUnauthorizedRequest(actionContext); 

     if (actionContext.RequestContext.Principal.Identity.IsAuthenticated) 
     { 
      actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Forbidden); 
     } 
    } 
} 

Notez que asp.net vous permettra de décorer votre contrôleur MVC avec un filtre API - il vient de gagner » t travaillez comme vous le souhaitez, donc gardez vos noms d'attribut explicites.

0

J'aime ce que Brian Vander Plaats publié, vient d'ajouter quelques améliorations:

/// <summary> 
/// Authorize or redirect to an unauthorized MVC action if the user does not have the required roles 
/// (an unauthenticated user will be redirected to the defualt sign in action) 
/// <para>Decorate an action or a controller like this [AuthorizeAndRedirect(Roles = "RoleName")]</para> 
/// </summary> 
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)] 
public class AuthorizeOrRedirectAttribute : System.Web.Mvc.AuthorizeAttribute 
{ 
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
    { 
     base.HandleUnauthorizedRequest(filterContext); 

     if (filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated) 
     { 
      var routeData = new RouteData(); 
      routeData.Values.Add("controller", "Error"); 
      routeData.Values.Add("action", "Unauthorized"); 
      filterContext.Result = new RedirectToRouteResult(routeData.Values); 
     } 
    } 
} 

/// <summary> 
/// Authorize or redirect to an unauthorized API action if the user does not have the required roles 
/// (an unauthenticated user will be redirected to the defualt sign in action) 
/// <para>Decorate an action or a controller like this [AuthorizeAndRedirect(Roles = "RoleName")]</para> 
/// </summary> 
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)] 
public class AuthorizeOrRedirectApiFilterAttribute : System.Web.Http.AuthorizeAttribute 
{ 
    protected override void HandleUnauthorizedRequest(HttpActionContext actionContext) 
    { 
     base.HandleUnauthorizedRequest(actionContext); 

     if (actionContext.RequestContext.Principal.Identity.IsAuthenticated) 
     { 
      actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized); 
     } 
    } 
}