MVC 5.0 [AllowAnonymous]和新的IAuthenticationFilter

时间:2013-10-23 19:56:20

标签: c# asp.net-mvc-5

当我创建一个新的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

HomeController使用[AllowAnonymous]属性进行修饰。

SecureController NOT [AllowAnonymous]属性修饰。

Index() ActionResult的{​​{1}}会显示一个带有简单按钮的视图。

当我点击按钮时,我对HomeController内的GetData()方法进行ajax调用,如下所示:

SecureController

毋庸置疑,当我点击该按钮时,我会触发$("#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,它恰好在授权过滤器之前被触发(这很棒并且让我们更精细控制如何区分未经过身份验证的用户(http 401)与经过身份验证且未经授权的用户(http 403)。

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

authentication filter

然后是属性:

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属性进行修饰。

在从VS 2013启动应用程序之前,我在CustomAuthenticationAttribute([AllowAnonymous]OnAuthentication)的两个方法中都设置了两个断点。

当我启动应用程序时,我点击了第一个断点(OnAuthenticationChallenge)。然后,令我惊讶的是,OnAuthentication Index() ActionResult内的代码被执行,并且只有在我返回View()之后,我才会点击{{ 1}}方法。

问题: 我有两个问题。

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

问题2) 为什么我的CustomAuthenticationAttribute [AllowAnonymous]方法中的代码在<{strong> Index()之后执行?只是意识到在我返回View()之后,HomeController中的代码会被执行吗?

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

也许我正在以错误的方式看待它。

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

此致 文斯

3 个答案:

答案 0 :(得分:7)

回答问题1:

[AllowAnnoymous]属性就像一个标志(它实际上没有实现逻辑)。在执行OnAuthorization期间,仅通过[Authorize]属性检查其存在。反编译[Authorize]属性会显示逻辑:

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

        if (skipAuthorization)
        {
            return;
        }

[AllowAnnonymous]永远不会自动地#&#39;绕过自定义属性中的代码......

因此,问题1的后半部分的答案是:是 - 如果您希望自定义属性对[AllowAnnonymous]的存在作出反应,那么您需要对其进行检查(类似于上面的内容)自定义[授权]属性中的[AllowAnnonymous]属性。

答案 1 :(得分:6)

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

据我所知[AllowAnonymous]属性与CustomAuthenticationAttribute无关。他们有不同的目的。 [AllowAnonymous]会在Authorization上下文中生效,但不会在Authentication上下文中生效。

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

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

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

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

答案 2 :(得分:5)

我需要在此提出第二个问题的澄清:

  

问题2)为什么代码在我的Index()方法中   HomeController在OnAuthentication之后执行?只有   我意识到在我返回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/

干杯。