18

当我创建一个新的 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没有用该[AllowAnonymous]属性修饰。

的显示一个带有简单按钮的视图Index() ActionResultHomeController

当我单击按钮时,我对 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_ authentication filter_ _已通过身份验证并且碰巧没有被授权(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]

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

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

问题: 我有两个问题。

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

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

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

也许我看错了。

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

真诚的文斯

4

3 回答 3

8

回答问题 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] 永远不会“自动”绕过自定义属性中的代码...

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

于 2014-04-21T14:51:13.747 回答
6

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

据我所知 [AllowAnonymous] 属性与 CustomAuthenticationAttribute 无关。他们有不同的目的。[AllowAnonymous] 在授权上下文中会产生影响,但在身份验证上下文中不会。

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

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

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

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

于 2013-10-25T12:46:03.460 回答
5

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

问题 2) 为什么我的 HomeController 的 Index() 方法中的代码在 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/

干杯。

于 2013-11-19T19:53:25.270 回答