MVC 5: Custom AuthorizeAttribute et Caching

j'essaie de trouver une solution pour implémenter custom System.Web.Mvc.AuthorizeAttribute en dérivant de et surcharger certaines de ses méthodes.

Chaque approche que j'essaie, je suis confronté à certains problèmes dans le mécanisme d'autorisation par défaut du MVC 5 qui m'empêche d'étendre correctement cela.

J'ai fait l'énorme recherche sur ce domaine sur SO et de nombreuses ressources dédiées, mais je ne pouvais pas obtenir une solution solide pour le scénario comme mon actuelle un.

première limitation:

ma logique d'autorisation a besoin de données supplémentaires comme les noms de controller et de méthode et les attributs qui leur sont appliqués plutôt qu'une partie limitée des données HttpContextBase est en mesure de fournir.

Exemple:

public override void OnAuthorization(AuthorizationContext filterContext)
{
    ...
    var actionDescriptor = filterContext.ActionDescriptor;
    var currentAction = actionDescriptor.ActionName;
    var currentController = actionDescriptor.ControllerDescriptor.ControllerName;

    var hasHttpPostAttribute = actionDescriptor.GetCustomAttributes(typeof(HttpPostAttribute), true).Any();
    var hasHttpGetAttribute = actionDescriptor.GetCustomAttributes(typeof(HttpGetAttribute), true).Any();

    var isAuthorized = securitySettingsProvider.IsAuthorized(
        currenPrincipal, currentAction, currentController, hasHttpPostAttribute, hasHttpGetAttribute);
    ...
}

C'est pourquoi je ne peux pas implémenter ma logique d'autorisation à l'intérieur du AuthorizeCore() méthode override puisqu'il n'y a que <!-Comme paramètre et ce dont j'ai besoin pour faire un décision d'autorisation est AuthorizationContext.

Ce qui m'amène à mettre ma logique d'autorisation à l' OnAuthorization() méthode de substitution comme dans l'exemple ci-dessus.

mais ici nous venons à la seconde:

AuthorizeCore() la méthode est appelée par le système de mise en cache pour prendre une décision d'autorisation si la requête courante doit être servie avec le cache ActionResult ou la méthode correspondante du contrôleur doit être utilisée pour créer une nouvelle ActionResult.

Donc on ne peut pas oublier le AuthorizeCore() et OnAuthorization() une seule.

Et ici, nous sommes de retour au point de départ:

comment prendre une décision d'autorisation pour le système de cache basée sur le HttpContextBase seulement si nous avons besoin de plus de données de la AuthorizationContext?

de nombreuses questions comme:

  • Comment sommes-nous censés mettre en œuvre correctement l' AuthorizeCore() dans ce cas?
  • dois-je implémenter ma propre mise en cache pour la laisser fournir données suffisantes pour le système d'autorisation? Et comment il peut être fait si oui?
  • Ou, devrais-je dire adieu à la mise en cache de tous contrôleur méthodes protégées avec ma coutume System.Web.Mvc.AuthorizeAttribute? Il faut dire ici que je vais utiliser mon custom System.Web.Mvc.AuthorizeAttribute comme un filtre global et c'est le complet adieu à la mise en cache si la réponse à cette la question est oui.

ainsi le principal la question ici:

quelles sont les approches possibles pour gérer une telle autorisation personnalisée et une mise en cache appropriée?

mise à JOUR 1 (des informations Supplémentaires à l'adresse des réponses possibles autour):

  1. il n'y a pas de garantie dans le MVC que chaque instance du AuthorizeAttribute servirait simple demande. Il peut être réutilisé pour de nombreuses requêtes (voir ici pour en savoir plus info):

    les attributs du filtre D'Action doivent être immuables, puisqu'ils peuvent être mis en cache. une partie de la canalisation et réutilisés. Selon l'emplacement de cet attribut est déclaré dans votre application, cela ouvre une attaque temporelle, qui une le visiteur de site malveillant pourrait alors exploiter pour se donner accès à toute action qu'il souhaite.

    En d'autres termes, AuthorizeAttribute MUST be immuable et ne doit pas partager l'état entre n'importe quelle méthode appels.

    En outre, dans le AuthorizeAttribute-que-global-filtre scénario, une seule instance de AuthorizeAttribute est utilisé pour servir toute la demande.

    Si vous pensez que vous enregistrez AuthorizationContext dans le OnAuthorization() pour une demande, vous êtes alors en mesure de l'obtenir dans la suite AuthorizeCore() pour la même demande, vous avez tort.

    En conséquence, vous prendriez une décision d'autorisation pour la requête actuelle basée sur le AuthorizationContext de l'autre demande.

  2. si a AuthorizeCore() est déclenché par la couche de cache, OnAuthorization() n'a jamais appelé pour la requête courante (référez-vous à sourcesAuthorizeAttribute à partir de CacheValidateHandler() vers AuthorizeCore()).

    En d'autres termes, si request va être servie en utilisant le cache ActionResult, seulement le AuthorizeCore() et pas OnAuthorization().

    Donc vous ne pouvez pas sauver!--5--> de toute façon dans ce cas.

par conséquent, partageant le AuthorizationContext entre le OnAuthorization() et AuthorizeCore() n'est pas l'option!

9
demandé sur Alexander Abakumov 2014-10-28 13:15:47

1 réponses

la méthode OnAuthorization est appelée avant la méthode AuthorizeCore. Ainsi, vous pouvez économiser le contexte actuel pour un traitement ultérieur:

public class MyAttribute: AuthorizeAttribute
{
    # Warning - this code doesn't work - see comments

    private AuthorizationContext _currentContext;

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

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
         // use _currentContext
    }    
}

Modifier

puisque cela ne fonctionnera pas comme Alexandre l'a souligné. La seconde option pourrait être de remplacer complètement la méthode OnAuthorization:

        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentNullException("filterContext");
            }

            if (OutputCacheAttribute.IsChildActionCacheActive(filterContext))
            {
                throw new InvalidOperationException(MvcResources.AuthorizeAttribute_CannotUseWithinChildActionCache);
            }

            bool skipAuthorization = filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true)
                                     || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true);

            if (skipAuthorization)
            {
                return;
            }

            if (AuthorizeCore(filterContext.HttpContext))
            {
                HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
                cachePolicy.SetProxyMaxAge(new TimeSpan(0));

                var actionDescriptor = filterContext.ActionDescriptor;
                var currentAction = actionDescriptor.ActionName;
                var currentController = actionDescriptor.ControllerDescriptor.ControllerName;

                var hasHttpPostAttribute = actionDescriptor.GetCustomAttributes(typeof(HttpPostAttribute), true).Any();
                var hasHttpGetAttribute = actionDescriptor.GetCustomAttributes(typeof(HttpGetAttribute), true).Any();
                // fill the data parameter which is null by default
                cachePolicy.AddValidationCallback(CacheValidateHandler, new { actionDescriptor : actionDescriptor, currentAction: currentAction, currentController: currentController, hasHttpPostAttribute : hasHttpPostAttribute, hasHttpGetAttribute: hasHttpGetAttribute  });
            }
            else
            {
                HandleUnauthorizedRequest(filterContext);
            }
        }

    private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
    {
        if (httpContext == null)
        {
            throw new ArgumentNullException("httpContext");
        }
        // the data will contain AuthorizationContext attributes
        bool isAuthorized = myAuthorizationLogic(httpContext, data);
        return (isAuthorized) ? HttpValidationStatus.Valid : httpValidationStatus.IgnoreThisRequest;

    }
6
répondu Marian Ban 2018-03-20 09:14:14