MVC 5.0 [AllowAnonymous] and the new IAuthenticationFilter

本文关键字:and the new IAuthenticationFilter AllowAnonymous MVC | 更新日期: 2023-09-27 18:36:33

当我创建一个新的 asp.net mvc 4.0应用程序时,我要做的第一件事就是创建和设置一个自定义授权global filter,如下所示:

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

然后我像这样创建CustomAuthorizationAttribute

//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)
        }
    }

我有两个控制器:HomeControllerSecureController

主控制器用 [AllowAnonymous] 属性进行装饰。

安全控制器不使用 [AllowAnonymous] 属性进行修饰。

HomeControllerIndex() ActionResult显示带有简单按钮的视图。

当我单击该按钮时,我对位于SecureController中的 GetData() 方法进行 ajax 调用,如下所示:

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

不用说,当我单击按钮时,我触发了CustomAuthorizationAttribute,因为它是一个全局过滤器,但也因为SecureController没有用 [AllowAnonymous] 属性装饰。

好的,我的介绍结束了...

随着 asp.net mvc 5.0 的引入,我们现在引入了一个新的authentication filter它恰好在授权过滤器之前触发(这很棒,让我们可以更精细地控制如何区分未经身份验证的用户 (http 401) 和经过身份验证的用户,并且碰巧未被授权 (http 403))。

为了尝试这个新authentication filter,我创建了一个新的 asp.net mvc 5.0(VS Express 2013 for Web),并从执行以下操作开始:

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

然后属性:

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

我创建了一个HomeController.HomeController装饰有[AllowAnonymous]属性。

在从VS 2013启动应用程序之前,我在自定义身份验证属性的两个方法(OnAuthenticationOnAuthenticationChallenge)中设置了两个断点。

启动应用程序时,我遇到了第一个断点(OnAuthentication)。然后,令我惊讶的是,我HomeController Index() ActionResult内的代码被执行,只有在我返回 View() 之后,我才在 OnAuthenticationChallenge() 方法上遇到断点。

问题:我有两个问题。

问题1)
我的印象是 [AllowAnonymous] 属性会自动绕过CustomAuthenticationAttribute中的任何代码,但我错了!我是否需要手动检查 [AllowAnonymous] 属性是否存在并跳过任何代码?

问题2)为什么我的HomeControllerIndex()方法中的代码在OnAuthentication执行?只是意识到在我返回 View() 后OnAuthenticationChallenge()中的代码会被执行吗?

我担心的是,如果用户未经过身份验证,我不希望执行 Index() 方法中的代码。

也许我看错了。

如果有人能帮我阐明这一点,那就太好了!

真诚地Vince

MVC 5.0 [AllowAnonymous] and the new IAuthenticationFilter

回答问题 1:

[AllowAnnoymous] 属性就像一个标志(它实际上没有实现逻辑)。它的存在只是在执行 OnAuthorization 期间由 [Authorize] 属性检查。反编译 [授权] 属性会显示逻辑:

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

[允许匿名] 永远不会"自动"绕过自定义属性中的代码......

因此,问题 1 后半部分的答案是:是的 - 如果您希望自定义属性对 [AllowAnnonymous] 的存在做出反应,则需要在自定义 [Authorize] 属性中对 [AllowAnnonymous] 属性进行检查(类似于上述检查)。

关于 :问题1)我的印象是[AllowAnonymous]属性会自动绕过我的自定义身份验证属性中的任何代码,但我错了!我是否需要手动检查 [AllowAnonymous] 属性是否存在并跳过任何代码?

据我所知,[AllowAnonymous]属性与CustomAuthenticationAttribute无关。它们有不同的目的。[允许匿名] 在授权上下文中有效,但在身份验证上下文中无效。

已实现身份验证筛选器以设置身份验证上下文。例如AuthenticationContext 为您提供用于执行身份验证的信息。 您可以使用此信息根据当前上下文做出身份验证决策。例如,您可以决定根据身份验证上下文将 ActionResult 修改为不同的结果类型,也可以决定根据身份验证上下文等更改当前主体。

OnAuthenticationChallenge 方法在 OnAuthentication 方法之后运行。 您可以使用 OnAuthenticationChallenge 方法对请求执行其他任务。

关于 :问题2)为什么我的 HomeController 的 Index() 方法中的代码在 OnAuthentication 之后被执行?只是意识到在我返回 View() 之后,OnAuthenticationChallenge() 中的代码会被执行吗?

这是预期的行为。由于您有一个全局注册的身份验证过滤器,因此第一件事是,在执行任何操作之前,它将首先触发 OnAuthentication 事件,正如您会注意到的那样。然后在执行索引之后进行 OnAuthenticationChallenge。一旦操作成功,与该操作相关的任何身份验证过滤器(即索引)都将运行 OnAuthenticationChallenge,以便它可以为操作结果做出贡献。正如您在 OnAuthenticationChallenge 的代码中所做的那样,您可以将 ActionResult 修改为 HttpUnauthorizedResult,这将与 ActionResult 协商。

我需要在这里澄清你的第二个问题:

问题2)为什么我的 Index() 方法中的代码是我的 主控制器在身份验证后执行?只到 意识到在我返回 View() 后,在 OnAuthenticationChallenge() 被执行?

如果要阻止用户在您的操作方法中执行代码,则实际上应该在 OnAuthentication 中测试凭据。OnAuthenticationChallenge 是使用自定义结果处理 401 的机会,例如将用户重定向到自定义控制器/操作并为他们提供进行身份验证的机会。

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
    }
}

下面是筛选器的更完整运行以及如何使用它:http://jameschambers.com/2013/11/working-with-iauthenticationfilter-in-the-mvc-5-framework/

干杯。