ASP.NET MVC自定义机制处理未经授权的请求

20

对于我的网站,我希望保护控制器或操作的以下行为:

如果用户发送普通请求,则重定向到登录页面(这很容易实现)

如果请求是Ajax类型Request.IsAjaxRequest()==true,则返回状态码401

如何创建此过滤器?


2
我找不到方法。在 AJAX 请求中,当我执行 Response.StatusCode = (int)HttpStatusCode.Unauthorized; 时,它会将我重定向到登录页面,这并不是我想要的。 - Rusi Nova
6个回答

29
 public class MyCustomAuthorize : AuthorizeAttribute
{
        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            //if ajax request set status code and end Response
            if (filterContext.HttpContext.Request.IsAjaxRequest())
            {
                filterContext.HttpContext.Response.StatusCode = 401;
                filterContext.HttpContext.Response.End();
            }

            base.HandleUnauthorizedRequest(filterContext);
        }
}
创建一个类似上面的过滤器,如果请求是通过ajax发出的并且未经授权,则返回状态码401。
如果您正在使用jQuery,可以按以下方式执行:
jQuery.ajax({
statusCode: {
    401: function() {
      alert('unauthrized');
    },

  /*other options*/
});

2
非常优雅的解决方案。比SO上其他的解决方案好多了。+1 - publicgk
8
这样做会抛出HTTPException:"服务器无法在发送HTTP头之后设置状态"。这个属性需要与其他操作过滤器按一定顺序运行吗? - Marchy
1
@Marchy 如果你删除 filterContext.HttpContext.Response.End(); 这行代码,异常就不会出现了。但我也不确定其他地方是否会受到影响,希望知道这行代码的目的是什么。 - Dmitry Efimenko
1
@Dmitry 如果您删除 filterContext.HttpContext.Response.End(); 则状态码将为200。 - Antipod
@Marchy,你忘记了最后一行吗?base.HandleUnauthorizedRequest(filterContext); 我曾经遇到过同样的问题,只是因为没有执行这一行。 - Amir Chatrbahr

4
除了已经接受的答案之外,我还需要添加以下代码行以防止FormsAuthentication重定向到登录页面:
filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
然后我删除了 filterContext.HttpContext.Response.End();
var unauthorizedResult = new JsonResult
{
    Data = new ErrorResult() {Success = 0, Error = "Forbidden"},
            JsonRequestBehavior = JsonRequestBehavior.AllowGet
    };
    // status code
    filterContext.HttpContext.Response.StatusCode = (int) HttpStatusCode.Unauthorized;
    // return data
    filterContext.Result = unauthorizedResult;
    filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
}

1

您可以只返回一个HttpUnauthorizedResult

注意:这可能会导致MVC框架将您返回到登录页面。

public ActionResult FailResult()
{
        return new HttpUnauthorizedResult();
}

1

你的问题不在于 AJAX 请求,而在于返回 HTTP 401 未授权响应,因为你使用了表单身份验证。这个响应代码告诉框架它应该用 HTTP 302 响应将用户代理重定向到你的登录页面。这就是为什么设置“正常”请求重定向很容易 - 它是自动完成的。
回答你的问题,我曾经遇到过类似的问题,最终的解决方案是不使用表单身份验证。我实现了一个自定义的授权属性,手动处理这两种情况。我不确定这是否是最佳方法,但它确实有效。我对其他人对这个解决方案的看法或其他解决方案感兴趣。
幸运的是,你仍然可以使用 FormsAuthentication 类来处理 cookie,但你必须从 Web.config 文件中删除表单身份验证配置。当用户登录时,你使用 FormsAuthentication.SetAuthCookie 来设置一个 cookie(你可能已经这样做了)。其次,在你的授权属性中,你从请求中获取 cookie,并使用 FormsAuthentication.Decrypt 解密它。如果它存在且有效,你就根据这个 cookie 在 HttpContext 中设置用户,因为表单身份验证不再为你完成这项工作。如果不存在,你要么重定向到登录页面,要么返回 401,这取决于它是否是 AJAX 调用。


1

您可以使用ajaxonly来限制对Ajax ActionResult的访问


0
一个简单的方法是在SignIn操作中进行检查。
public ActionResult SignIn()
{
    if (Request.IsAjaxRequest())
    {
        // you could return a partial view that has this script instead
        return Content("<script>window.location = '" + Url.Action("SignIn", "Account") + "'</script>");
    }
   ...
   return View();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接