MVC 5.0 [AllowAnonymous] et le nouveau IAuthenticationFilter

Quand je crée un nouveau asp.net mvc 4.0 de l'application, un des première chose que je fais, est créer et définir une autorisation personnalisée global filter comme ceci:

//FilterConfig.cs
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
 //filters.Add(new HandleErrorAttribute());
 filters.Add(new CustomAuthorizationAttribute());
}

puis je crée le CustomAuthorizationAttribute comme ceci:

//CustomAuthorizationAttribute.cs
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAjaxRequest())  
        {
            //Handle AJAX requests
            filterContext.HttpContext.Response.StatusCode = 403;
            filterContext.Result = new JsonResult { JsonRequestBehavior = JsonRequestBehavior.AllowGet };
        }
        else
        {
            //Handle regular requests
            base.HandleUnauthorizedRequest(filterContext); //let FormsAuthentication make the redirect based on the loginUrl defined in the web.config (if any)
        }
    }

j'ai deux contrôleurs: HomeController et SecureController

le HomeController est décoré avec le [AllowAnonymous] l'attribut.

le contrôleur de sécurité est décoré avec le [AllowAnonymous] attribut.

Index() ActionResultHomeController affiche une Vue avec un simple bouton.

quand je clique sur le bouton, je fais un appel ajax à une méthode GetData () qui vit à l'intérieur du SecureController comme ceci:

$("#btnButton").click(function () {
    $.ajax({
        url: '@Url.Action("GetData", "Secure")',
        type: 'get',
        data: {param: "test"},
        success: function (data, textStatus, xhr) {
            console.log("SUCCESS GET");
        }
    });
});

inutile de dire, quand je clique sur le bouton, je déclenche le CustomAuthorizationAttribute parce que c'est un filtre global, mais aussi parce que le SecureController n'est pas décoré avec le [AllowAnonymous] l'attribut.

Ok, j'en ai fini avec mon introduction...

Avec l'introduction de asp.net mvc 5.0, nous sommes maintenant à un nouveau authentication filter qui se déclenche avant le filtre d'autorisation (ce qui est génial et nous donne un contrôle plus granulaire sur la façon dont je peux différencier un utilisateur qui N'est pas authentifié (http 401) d'un utilisateur qui est authentifié et qui se trouve ne pas être autorisé (http 403)).

afin de donner ce nouveau authentication filter un essai, j'ai créé un nouveau asp.net mvc 5.0 (VS Express 2013 Pour Web) et a commencé par faire ce qui suit:

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    //filters.Add(new HandleErrorAttribute());
    filters.Add(new CustomAuthenticationAttribute());   //Notice I'm using the word Authentication and not Authorization
}

, l'attribut:

public class CustomAuthenticationAttribute : ActionFilterAttribute, IAuthenticationFilter 
{
    public void OnAuthentication(AuthenticationContext filterContext)
    {

    }

    public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
    {
        var user = filterContext.HttpContext.User;
        if (user == null || !user.Identity.IsAuthenticated)
        {
            filterContext.Result = new HttpUnauthorizedResult();
        }
    }
}

j'ai créé un HomeController. HomeController est décoré avec le [AllowAnonymous] l'attribut.

avant de lancer L'application à partir de VS 2013, j'ai mis deux points de rupture à l'intérieur des deux méthodes de mon CustomAuthenticationAttribute (OnAuthentication et OnAuthenticationChallenge).

quand je lance l'application, j'atteins le premier point de rupture (OnAuthentication). Alors, à mon surprise, le code dans le Index() ActionResult mon HomeController se fait exécuter et ce n'est qu'après avoir retourné le View() que j'ai atteint le point de rupture sur le OnAuthenticationChallenge() méthode.

Questions: J'ai deux questions.

Question 1)

J'étais sous l'impression que l' [AllowAnonymous] attribut automatiquement contourner le code dans mon CustomAuthenticationAttribute mais j'avais tort! Je dois manuellement case pour les l'existence de l' [AllowAnonymous] attribuez et sautez tout code?

Question 2) Pourquoi le code à l'intérieur de mon Index() méthode de mon HomeController se fait exécuter aprèsOnAuthentication? Seulement pour réaliser qu'après que je renvoie View () le code à l'intérieur du OnAuthenticationChallenge() est exécuté?

ma préoccupation est que je ne veux pas le code de la Index() méthode pour obtenir exécuté si l'utilisateur n'est PAS authentifié.

peut-être que je regarde ça de travers façon.

Si quelqu'un peut m'aider à faire la lumière sur ce, je veux bien!

sincèrement Vince

16
demandé sur Askolein 2013-10-23 23:56:19

3 réponses

En ce qui concerne : Question 1) J'avais l'impression que l'attribut [AllowAnonymous] contournerait automatiquement n'importe quel code dans mon attribut CustomAuthenticationAttribute mais j'avais tort! Dois-je vérifier manuellement l'existence de l'attribut [AllowAnonymous] et sauter tout code?

pour autant que je sache, l'attribut [AllowAnonymous] n'a rien à voir avec un attribut CustomAuthenticationAttribute. Ils ont des buts différents. [AllowAnonymous] aurait un effet dans un contexte D'autorisation, mais pas dans un contexte d'authentification.

le filtre D'authentification a été implémenté pour configurer le contexte d'authentification. Par exemple, Authentificationcontext vous fournit des informations pour effectuer l'authentification. Vous pouvez utiliser cette information pour prendre des décisions d'authentification en fonction du contexte actuel. Par exemple, vous pouvez décider de modifier le résultat ActionResult à un type de résultat différent basé sur le contexte d'authentification, ou vous pouvez décider de changer le principal courant en fonction du contexte d'authentification, etc.

la méthode OnAuthenticationChallenge suit la méthode OnAuthentication. Vous pouvez utiliser la méthode OnAuthenticationChallenge pour effectuer des tâches supplémentaires sur la requête.

En ce qui concerne : Question 2) Pourquoi le code dans ma méthode Index() de mon Controller HomeController est-il exécuté après L'OnAuthentication? Seulement pour réaliser qu'après que je renvoie View () le code à l'intérieur du OnAuthenticationChallenge () est exécuté?

C'est le comportement attendu. Depuis que vous avez un filtre D'authentification globalement enregistré, la toute première chose est que, avant qu'une action ne s'exécute, elle lancerait d'abord l'événement OnAuthentication comme vous l'auriez remarqué. Ensuite, L'OnAuthenticationChallenge après L'exécution de L'Index. Une fois l'Action terminée, tout filtre d'authentification correspondant à cette Action (I. e Index), relèverait le défi Onauthenticationchall de sorte qu'il peut contribuer au résultat de l'action. Comme vous l'avez dans votre code pour OnAuthenticationChallenge, vous pouvez modifier le résultat de L'action à un résultat autorisé par Httpun. ceci serait négocié avec le résultat de l'action.

6
répondu Spock 2013-10-25 12:46:03

en réponse à la Question 1:

l'attribut [AllowAnnoymous] agit comme un drapeau (il n'a en fait aucune logique d'implémentation). Sa présence est simplement vérifiée par l'attribut [Authorize] lors de l'exécution de L'OnAuthorization. La décomposition de l'attribut [Authorize] révèle la logique:

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

        if (skipAuthorization)
        {
            return;
        }

[AllowAnnonymous] ne contournerait jamais "automatiquement" le code dans votre attribut personnalisé...

donc la réponse à la seconde partie de la Question 1 est: oui - si vous voulez que votre attribut personnalisé réagisse à la présence du [AllowAnnonymous], alors vous devez implémenter une vérification (similaire à celle ci-dessus) pour l'attribut [AllowAnnonymous] dans votre attribut personnalisé [Authorize].

7
répondu JTech 2014-04-21 14:51:13

j'ai besoin de donner des éclaircissements ici à votre deuxième question:

Question 2) Pourquoi le code est-il dans ma méthode Index() de HomeController est exécuté après L'OnAuthentication? Seulement se rendre compte qu'après que je retourne View () faire le code à l'intérieur de la OnAuthenticationChallenge () est exécuté?

Vous devriez en fait tester les informations d'identification dans OnAuthentication si vous voulez empêcher l'utilisateur d'exécuter le code dans votre action méthode. OnAuthenticationChallenge est votre chance de gérer la 401 avec un résultat personnalisé, tel que la redirection de l'utilisateur vers un contrôleur/action personnalisé et leur donner une chance de s'authentifier.

public class CustomAuthenticationAttribute : ActionFilterAttribute, IAuthenticationFilter 
{
    public void OnAuthentication(AuthenticationContext filterContext)
    {
            var user = filterContext.HttpContext.User;
        if (user == null || !user.Identity.IsAuthenticated)
        {
            filterContext.Result = new HttpUnauthorizedResult();
        }
    }

    public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
    {
        // modify filterContext.Result to go somewhere special...if you do
        // nothing here they will just go to the site's default login
    }
}

voici un aperçu plus complet du filtre et de la façon dont vous pourriez l'utiliser: http://jameschambers.com/2013/11/working-with-iauthenticationfilter-in-the-mvc-5-framework/

Cheers.

5
répondu MisterJames 2013-12-16 19:34:27