MVC 5.0 和新的IAuthenticationFilter
当我创建一个新的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) } }
我有两个控制器: HomeController
和SecureController
HomeController使用[AllowAnonymous]
属性进行修饰。
SecureController未使用[AllowAnonymous]
属性进行修饰。
HomeController
的Index() 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
因为它是一个全局filter,但也因为SecureController
没有使用[AllowAnonymous]
属性进行修饰。
好的,我完成了我的介绍……
随着asp.net mvc 5.0
的推出,我们现在引入了一个新的authentication filter
,它恰好在授权filter之前被触发(这很好,并且我们可以更细粒度地控制如何区分未经过身份validation的用户(来自经过IS认证且恰好未被授权的用户的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启动应用程序之前,我在CustomAuthenticationAttribute( OnAuthentication
和OnAuthenticationChallenge
)的两个方法中都设置了两个断点。
当我启动应用程序时,我点击了第一个断点( OnAuthentication
)。 然后,令我惊讶的是,我的HomeController
的Index() ActionResult
中的代码被执行,并且只有在我返回View()之后才能执行OnAuthenticationChallenge()
方法的OnAuthenticationChallenge()
。
问题:我有两个问题。
问题1)
我的印象是[AllowAnonymous]
属性会自动绕过CustomAuthenticationAttribute
任何代码,但我错了! 我是否需要手动检查是否存在[AllowAnonymous]
属性并跳过任何代码?
问题2)为什么我的HomeController
Index()
方法中的代码在OnAuthentication
之后执行? 只是意识到在我返回View()后, OnAuthenticationChallenge()
内的代码会被执行吗?
我担心的是,如果用户未经过身份validation,我不希望执行Index()
方法中的代码。
也许我正在以错误的方式看待这个问题。
如果有人能帮助我阐明这一点,那就太好了!
真诚的文斯
关于: 问题1)我的印象是[AllowAnonymous]属性会自动绕过CustomAuthenticationAttribute中的任何代码,但我错了! 我是否需要手动检查是否存在[AllowAnonymous]属性并跳过任何代码?
据我所知[AllowAnonymous]属性与CustomAuthenticationAttribute无关。 他们有不同的目的。 [AllowAnonymous]会在Authorization上下文中生效,但不会在Authentication上下文中生效。
已实施身份validationfilter以设置身份validation上下文。 例如,AuthenticationContext为您提供执行身份validation的信息。 您可以使用此信息根据当前上下文做出身份validation决策。 例如,您可以决定根据身份validation上下文将ActionResult修改为不同的结果类型,或者您可以决定根据身份validation上下文等更改当前主体。
OnAuthenticationChallenge方法在OnAuthentication方法之后运行。 您可以使用OnAuthenticationChallenge方法对请求执行其他任务。
关于: 问题2)为什么我的HomeController的Index()方法中的代码在OnAuthentication之后执行? 只是意识到在我返回View()后,OnAuthenticationChallenge()内的代码会被执行吗?
这是预期的行为。 由于您拥有全局注册的身份validationfilter,因此首先要做的是,在执行任何操作之前,它会首先触发OnAuthentication事件,就像您已经注意到的那样。 然后执行索引后的OnAuthenticationChallenge。 一旦Action成功完成任何与Action(即Index)相关的身份validationfilter,就会运行OnAuthenticationChallenge,以便它可以为动作结果做出贡献。 正如您在OnAuthenticationChallenge的代码中所做的那样,您可以将ActionResult修改为HttpUnauthorizedResult,这将与ActionResult协商。
在回答问题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]执行检查(类似于上面的内容)您的自定义[授权]属性中的属性。
我需要在此提出第二个问题的澄清:
问题2)为什么我的HomeController的Index()方法中的代码在OnAuthentication之后执行? 只是意识到在我返回View()后,OnAuthenticationChallenge()内的代码会被执行吗?
如果要阻止用户执行操作方法中的代码,您实际上应该在OnAuthentication中测试凭据。 OnAuthenticationChallenge是您使用自定义结果处理401的机会,例如将用户重定向到自定义控制器/操作并让他们有机会进行身份validation。
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 } }
以下是filter的更完整的演练以及如何使用它: http : //jameschambers.com/2013/11/working-with-iauthenticationfilter-in-the-mvc-5-framework/
干杯。