当我创建一个新的asp.net mvc 4.0应用程序时,我做的第一件事就是创建并设置一个自定义授权,global filter如下所示:
//FilterConfig.cs
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
//filters.Add(new HandleErrorAttribute());
filters.Add(new CustomAuthorizationAttribute());
}
Run Code Online (Sandbox Code Playgroud)
然后我创建了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)
}
}
Run Code Online (Sandbox Code Playgroud)
我有两个控制器:HomeController和SecureController
HomeController使用[AllowAnonymous]属性进行装饰.
该SecureController是不具有装饰[AllowAnonymous]属性.
该Index() ActionResult对的HomeController一个简单的按钮显示视图.
当我单击按钮时,我对一个生活在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");
}
});
});
Run Code Online (Sandbox Code Playgroud)
毋庸置疑,当我单击按钮时,我会触发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
}
Run Code Online (Sandbox Code Playgroud)
那么属性:
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
我创造了一个HomeController.该HomeController装饰与[AllowAnonymous]属性.
在从VS 2013启动应用程序之前,我在CustomAuthenticationAttribute(OnAuthentication和OnAuthenticationChallenge)的两个方法中都设置了两个断点.
当我启动应用程序时,我点击了第一个断点(OnAuthentication).然后,让我惊讶的是,内码Index() ActionResult我的HomeController 被执行,只有后,我返回查看()我击中了破发点OnAuthenticationChallenge()方法.
问题: 我有两个问题.
问题1)
我的印象是该[AllowAnonymous]属性会自动绕过我的任何代码,CustomAuthenticationAttribute但我错了!我是否需要手动检查[AllowAnonymous]属性是否存在并跳过任何代码?
问题2)为什么是我里面的代码Index()的方法我HomeController被执行后的OnAuthentication?只是意识到在我返回View()之后,执行内部的代码OnAuthenticationChallenge()执行了吗?
我担心的是,Index()如果用户未经过身份验证,我不希望方法中的代码被执行.
也许我正在以错误的方式看待这个问题.
如果有人能帮助我阐明这一点,那就太好了!
真诚的文斯
在回答问题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;
}
Run Code Online (Sandbox Code Playgroud)
[AllowAnnonymous]永远不会"自动"绕过自定义属性中的代码......
因此问题1的后半部分的答案是:是 - 如果您希望自定义属性对[AllowAnnonymous]的存在作出反应,那么您需要为[AllowAnnonymous]执行检查(类似于上面的内容)您的自定义[授权]属性中的属性.
关于: 问题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)为什么我的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
}
}
Run Code Online (Sandbox Code Playgroud)
以下是过滤器的更完整的贯穿内容以及如何使用它:http://jameschambers.com/2013/11/working-with-iauthenticationfilter-in-the-mvc-5-framework/
干杯.