在Asp.Net MVC 2中有一种更好的方法来返回401状态代码而无需获得auth重定向

时间:2010-05-12 07:07:08

标签: asp.net-mvc rest asp.net-mvc-2

我的网站的一部分有一个轻量级的xml / json REST API。我的大部分网站都在表单身份验证之后,但只有部分API操作需要身份验证。

我有一个自定义AuthorizeAttribute用于我的API,用于检查某些权限,当它失败时会产生401.一切都很好,除非我使用表单auth,Asp.net方便地将其转换为302重定向到我的登录页面。

我看过一些以前的问题似乎有点不知所措,要么返回403,要么将一些逻辑放在global.asax protected void Application_EndRequest() 这将基本上将302转换为401,满足任何标准。

我现在正在做的有点像其中一个问题,但不是检查302的 Application_EndRequest(),而是让我的授权属性返回 666 这向我表明我需要将其设置为401。

这是我的代码:

protected void Application_EndRequest()
{
  if (Context.Response.StatusCode == MyAuthAttribute.AUTHORIZATION_FAILED_STATUS)
   {   
       //check for 666 - status code of hidden 401
        Context.Response.StatusCode = 401;
    }
 }

即使这有效,我的问题是Asp.net MVC 2中有什么东西会阻止我这么做吗?或者,总的来说还有更好的方法吗?我认为对于任何做REST api的人或只是在控制器中执行ajax请求的人来说,这会有很多。你想要的最后一件事是做一个请求并获取登录页面的内容而不是json。

5 个答案:

答案 0 :(得分:5)

如何使用自定义过滤器修饰控制器/操作:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class RequiresAuthenticationAttribute : FilterAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        var user = filterContext.HttpContext.User;
        if (!user.Identity.IsAuthenticated)
        {
            filterContext.HttpContext.Response.StatusCode = 401;
            filterContext.HttpContext.Response.End();
        }
    }
}

并在您的控制器中:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    [RequiresAuthentication]
    public ActionResult AuthenticatedIndex()
    {
        return View();
    }
}

答案 1 :(得分:2)

另一种方法是实现自定义ActionResult。在我的情况下,无论如何我想要一个,因为我想要一种简单的方法来发送带有自定义标头和响应代码的数据(对于REST API。)我发现了做DelegatingActionResult的想法并简单地添加了一个调用到Response.End()。结果如下:

public class DelegatingActionResult : ActionResult
{
    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
            throw new ArgumentNullException("context");

        Command(context);
        // prevent ASP.Net from hijacking our headers
        context.HttpContext.Response.End();
    }

    private readonly Action<ControllerContext> Command;

    public DelegatingActionResult(Action<ControllerContext> command)
    {
        if (command == null)
            throw new ArgumentNullException("command");

        Command = command;
    }
}

答案 2 :(得分:0)

我发现的最简单,最干净的解决方案是使用jQuery.ajaxSuccess()事件注册回调,并检查“X-AspNetMvc-Version”响应头。

我的应用程序中的每个jQuery Ajax请求都由Mvc处理,所以如果标题丢失,我知道我的请求已被重定向到登录页面,我只是重新加载页面以进行顶级重定向:

 $(document).ajaxSuccess(function(event, XMLHttpRequest, ajaxOptions) {
    // if request returns non MVC page reload because this means the user 
    // session has expired
    var mvcHeaderName = "X-AspNetMvc-Version";
    var mvcHeaderValue = XMLHttpRequest.getResponseHeader(mvcHeaderName);

    if (!mvcHeaderValue) {
        location.reload();
    }
});

页面重新加载可能会导致一些Javascript错误(取决于您对Ajax响应所做的操作),但在大多数情况下,如果关闭调试,用户将永远不会看到这些错误。

如果您不想使用内置标题,我确信您可以轻松添加自定义标题,并遵循相同的模式。

答案 3 :(得分:0)

TurnOffTheRedirectionAtIIS

从MSDN,This article解释了如何避免401响应的重定向:)。

举例:

  

使用IIS管理器,右键单击   WinLogin.aspx文件,单击属性,   然后转到“自定义错误”选项卡   编辑各种401错误和   分配自定义重定向。   不幸的是,这种重定向必须   是一个静态文件 - 它不会处理   一个ASP.NET页面。我的解决方案是   重定向到静态Redirect401.htm   文件,具有完整的物理路径,   其中包含javascript或a   元标记,重定向到真实   ASP.NET登录表单,命名为   WebLogin.aspx。请注意,你输了   这些原始的ReturnUrl   重定向,因为IIS错误   重定向需要静态html   没有动态的文件,所以你会   必须稍后处理。

希望它对你有所帮助。

答案 4 :(得分:0)

我还在使用结束请求技术,所以我想我会答应,但实际上 either of the options listed here通常是我所说的迄今为止最好的答案。

protected void Application_EndRequest()
{
  if (Context.Response.StatusCode == MyAuthAttribute.AUTHORIZATION_FAILED_STATUS)
   {   
       //check for 666 - status code of hidden 401
        Context.Response.StatusCode = 401;
    }
 }