未经授权的webapi调用返回login页面,而不是401

如何configuration我的mvc / webapi项目,以便从剃刀视图调用的webapi方法在未经授权的情况下不会返回login页面?

它的MVC5应用程序,也有WebApi控制器通过JavaScript调用。

以下两种方法

[Route("api/home/LatestProblems")] [HttpGet()] public List<vmLatestProblems> LatestProblems() { // Something here } [Route("api/home/myLatestProblems")] [HttpGet()] [Authorize(Roles = "Member")] public List<vmLatestProblems> mylatestproblems() { // Something there } 

通过以下angular码调用:

 angular.module('appWorship').controller('latest', ['$scope', '$http', function ($scope,$http) { var urlBase = baseurl + '/api/home/LatestProblems'; $http.get(urlBase).success(function (data) { $scope.data = data; }).error(function (data) { console.log(data); }); $http.get(baseurl + '/api/home/mylatestproblems') .success(function (data) { $scope.data2 = data; }).error(function (data) { console.log(data); }); }] ); 

所以我没有login,第一个方法成功返回数据。 第二种方法返回(在成功函数中)包含相当于login页面的数据。 即,如果您要求一个控制器操作,并在[Authorize]上加盖,而您没有login,您将在mvc中获得什么。

我希望它返回一个401未经授权的,以便我可以显示不同的数据为基础的用户,如果他们login或不。 理想情况下,如果用户login,我希望能够访问控制器的用户属性,所以我可以返回特定于该成员的数据。

更新:由于以下的build议似乎没有任何工作(身份或WebAPI的变化)我在github上创build了一个原始的例子,应该说明问题。

有两个AuthorizeAttribute实现,你需要确保你引用了正确的Web API。 有用于Web API的System.Web.Http.AuthorizeAttribute和用于具有视图的控制器的System.Web.Mvc.AuthorizeAttribute 。 如果授权失败, Http.AuthorizeAttribute将返回401错误, Mvc.AuthorizeAttribute将redirect到login页面。

更新11/26/2013

所以看起来,像Brock Allen 在他的文章中指出的那样, MVC 5看起来已经发生了巨大的变化。 我猜OWINpipe道接pipe并引入了一些新的行为。 现在,当用户没有被授权时,返回的状态是200,并在HTTP头中包含以下信息。

 X-Responded-JSON: {"status":401,"headers":{"location":"http:\/\/localhost:59540\/Account\/Login?ReturnUrl=%2Fapi%2FTestBasic"}} 

您可以在客户端更改您的逻辑,以检查头中的这些信息,以确定如何处理此问题,而不是在错误分支上查找401状态。

我尝试通过在OnAuthorizationHandleUnauthorizedRequest方法中设置响应中的状态来覆盖自定义的AuthorizeAttribute中的此行为。

 actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized); 

但是这不起作用。 新的pipe道必须稍后获取此响应,并将其修改为我之前获得的相同响应。 抛出一个HttpException无效,因为它只是变成了500错误状态。

我testing了Brock Allen的解决scheme,当我使用jQuery ajax调用时,它确实工作。 如果它不适合你,我的猜测是,因为你正在使用angular。 用Fiddler运行你的testing,看看下面是否在你的头部。

 X-Requested-With: XMLHttpRequest 

如果不是,那就是问题所在。 我不熟悉angular度,但如果它允许您插入自己的标题值,然后将其添加到您的ajax请求,它可能会开始工作。

Brock Allen在使用Cookie身份validation和OWIN时,如何为ajax调用返回401有一篇很好的博客文章。 http://brockallen.com/2013/10/27/using-cookie-authentication-middleware-with-web-api-and-401-response-codes/

把它放在Startup.Auth.cs文件的ConfigureAuth方法中:

 app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = new PathString("/Account/Login"), Provider = new CookieAuthenticationProvider { OnApplyRedirect = ctx => { if (!IsAjaxRequest(ctx.Request)) { ctx.Response.Redirect(ctx.RedirectUri); } } } }); private static bool IsAjaxRequest(IOwinRequest request) { IReadableStringCollection query = request.Query; if ((query != null) && (query["X-Requested-With"] == "XMLHttpRequest")) { return true; } IHeaderDictionary headers = request.Headers; return ((headers != null) && (headers["X-Requested-With"] == "XMLHttpRequest")); } 

如果你正在asp.net MVC网站里面添加asp.net WebApi,你可能想要对某些请求进行未经授权的响应。 但是,然后ASP.NET基础架构发挥作用,当您尝试将响应状态代码设置为HttpStatusCode.Unauthorized时,您将获得302redirect到login页面。

如果您在这里使用asp.net身份validation和基于owin的身份validation,则可以使用以下代码来解决该问题:

 public void ConfigureAuth(IAppBuilder app) { app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = new PathString("/Account/Login"), Provider = new CookieAuthenticationProvider() { OnApplyRedirect = ctx => { if (!IsApiRequest(ctx.Request)) { ctx.Response.Redirect(ctx.RedirectUri); } } } }); app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie); } private static bool IsApiRequest(IOwinRequest request) { string apiPath = VirtualPathUtility.ToAbsolute("~/api/"); return request.Uri.LocalPath.StartsWith(apiPath); } 

当OWIN总是从WebApiredirect401login页面时,我得到了相同的情况。我们的Web API不仅支持来自Angular的Ajax调用,还支持Mobile,Win Form调用。 因此,检查请求是否是ajax请求的解决scheme并不是真正的分类。

我select另一种方法是注入新的标题响应:如果响应来自webApi,则取消Suppress-Redirect 。 实现在处理程序上:

 public class SuppressRedirectHandler : DelegatingHandler { /// <summary> protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { return base.SendAsync(request, cancellationToken).ContinueWith(task => { var response = task.Result; response.Headers.Add("Suppress-Redirect", "True"); return response; }, cancellationToken); } } 

并在WebApi的全局级别注册该处理程序:

 config.MessageHandlers.Add(new SuppressRedirectHandler()); 

因此,在OWIN启动时,您可以检查响应头是否有Suppress-Redirect

 public void Configuration(IAppBuilder app) { app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationMode = AuthenticationMode.Active, AuthenticationType = DefaultApplicationTypes.ApplicationCookie, ExpireTimeSpan = TimeSpan.FromMinutes(48), LoginPath = new PathString("/NewAccount/LogOn"), Provider = new CookieAuthenticationProvider() { OnApplyRedirect = ctx => { var response = ctx.Response; if (!IsApiResponse(ctx.Response)) { response.Redirect(ctx.RedirectUri); } } } }); } private static bool IsApiResponse(IOwinResponse response) { var responseHeader = response.Headers; if (responseHeader == null) return false; return (responseHeader["Suppress-Redirect"] == "True"); } 

在以前的ASP.NET版本中,你必须做大量的工作来实现这个function。

好消息是,因为你正在使用ASP.NET 4.5。 您可以使用新的HttpResponse.SuppressFormsAuthenticationRedirect属性禁用表单身份validationredirect。

Global.asax

 protected void Application_EndRequest(Object sender, EventArgs e) { HttpApplication context = (HttpApplication)sender; context.Response.SuppressFormsAuthenticationRedirect = true; } 

编辑 :你可能也想看看这个由谢尔盖Zwezdin 这个文章有一个更完善的方式来完成你正在尝试做的事情。

下面粘贴了相关的代码片段和作者旁白。 原作者的代码和叙述 – 谢尔盖Zwezdin 。

首先,让我们确定当前的HTTP请求是否是AJAX请求。 如果是,我们应该禁用HTTP 302replaceHTTP 401:

 public class ApplicationAuthorizeAttribute : AuthorizeAttribute { protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { var httpContext = filterContext.HttpContext; var request = httpContext.Request; var response = httpContext.Response; if (request.IsAjaxRequest()) response.SuppressFormsAuthenticationRedirect = true; base.HandleUnauthorizedRequest(filterContext); } } 

其次 – 让我们添加一个条件::如果用户身份validation,然后我们将发送HTTP 403; 和HTTP 401否则。

 public class ApplicationAuthorizeAttribute : AuthorizeAttribute { protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { var httpContext = filterContext.HttpContext; var request = httpContext.Request; var response = httpContext.Response; var user = httpContext.User; if (request.IsAjaxRequest()) { if (user.Identity.IsAuthenticated == false) response.StatusCode = (int)HttpStatusCode.Unauthorized; else response.StatusCode = (int)HttpStatusCode.Forbidden; response.SuppressFormsAuthenticationRedirect = true; response.End(); } base.HandleUnauthorizedRequest(filterContext); } } 

做得好。 现在我们应该用这个新的filter来replace标准AuthorizeAttribute的所有使用。 这可能不适用于代码审美的男人。 但我不知道任何其他的方式。 如果你有,请让我们去评论。

最后,我们应该做的 – 在客户端添加HTTP 401/403处理。 我们可以在jQuery中使用ajaxError来避免代码重复:

 $(document).ajaxError(function (e, xhr) { if (xhr.status == 401) window.location = "/Account/Login"; else if (xhr.status == 403) alert("You have no enough permissions to request this resource."); }); 

结果 –

  • 如果用户没有通过身份validation,那么在任何AJAX调用之后,他将被redirect到一个login页面。
  • 如果用户被authentication,但没有足够的权限,那么他会看到用户友好的erorr消息。
  • 如果用户通过身份validation并具有足够的权限,则不会有任何错误,HTTP请求将照常进行。

自己使用Azure Active Directory集成,使用CookieAuthentication中间件的方法对我无效。 我必须做到以下几点:

 app.UseOpenIdConnectAuthentication( new OpenIdConnectAuthenticationOptions { ... Notifications = new OpenIdConnectAuthenticationNotifications { ... RedirectToIdentityProvider = async context => { if (!context.Request.Accept.Contains("html")) { context.HandleResponse(); } }, ... } }); 

如果请求来自浏览器本身(而不是AJAX调用),那么Accept头将在其中包含stringhtml 。 只有当客户端接受HTML时,我才会考虑一个有用的redirect。

我的客户端应用程序可以处理401通知用户应用程序没有更多的访问权限,需要重新加载才能再次login。

如果您正在从您的MVC项目中运行Web API ,则需要创build一个自定义的AuthorizeAttribute以应用于您的API方法。 在IsAuthorized override你需要获取当前的HttpContext以防止redirect,如下所示:

  protected override bool IsAuthorized(HttpActionContext actionContext) { if (Thread.CurrentPrincipal.Identity.Name.Length == 0) { // If using Basic Authentication, do that here } if (Thread.CurrentPrincipal.Identity.Name.Length == 0) { var response = HttpContext.Current.Response; response.SuppressFormsAuthenticationRedirect = true; response.StatusCode = (int)System.Net.HttpStatusCode.Forbidden; response.End(); } return base.IsAuthorized(actionContext); } 

我也有一个MVC5应用程序(System.Web)与WebApi(使用OWIN),只是想防止从WebApi 401响应被改为302响应。

什么对我来说是创build一个定制版本的WebApi AuthorizeAttribute像这样:

 public class MyAuthorizeAttribute : System.Web.Http.AuthorizeAttribute { protected override void HandleUnauthorizedRequest(HttpActionContext actionContext) { base.HandleUnauthorizedRequest(actionContext); HttpContext.Current.Response.SuppressFormsAuthenticationRedirect = true; } } 

并用它来代替标准的WebApi AuthorizeAttribute。 我使用标准的MVC AuthorizeAttribute来保持MVC行为不变。

在尝试避免redirect到login页面之后,我意识到这实际上对于Authorize属性是非常合适的。 这是说去获得授权。 相反,对于没有授权的Api调用,我只是不想透露任何信息是黑客。 这个目标更容易实现直接通过添加一个新的属性派生的授权,而不是隐藏的内容为404错误:

 public class HideFromAnonymousUsersAttribute : AuthorizeAttribute { protected override void HandleUnauthorizedRequest(HttpActionContext actionContext) { actionContext.Response = ActionContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, "Access Restricted"); } } 

如果你想捕获Content-Type == application / json,你可以使用下面的代码:

 private static bool IsAjaxRequest(IOwinRequest request) { IReadableStringCollection queryXML = request.Query; if ((queryXML != null) && (queryXML["X-Requested-With"] == "XMLHttpRequest")) { return true; } IReadableStringCollection queryJSON = request.Query; if ((queryJSON != null) && (queryJSON["Content-Type"] == "application/json")) { return true; } IHeaderDictionary headersXML = request.Headers; var isAjax = ((headersXML != null) && (headersXML["X-Requested-With"] == "XMLHttpRequest")); IHeaderDictionary headers = request.Headers; var isJson = ((headers != null) && (headers["Content-Type"] == "application/json")); return isAjax || isJson; } 

问候!!

我很难得到OnAuthorization / HandleUnauthorizedRequest方法中的状态码和文本响应。 这对我来说是最好的解决scheme:

  actionContext.Response = new HttpResponseMessage() { StatusCode = HttpStatusCode.Forbidden, Content = new StringContent(unauthorizedMessage) }; 

只需安装下面的NeGet包

安装程序包Microsoft.AspNet.WebApi.Owin

在WebApiConfig文件中写入以下代码。

 public static class WebApiConfig { public static void Register(HttpConfiguration config) { //Web API configuration and services //Configure Web API to use only bearer token authentication. config.SuppressDefaultHostAuthentication(); config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType)); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional } ); config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html")); config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("multipart/form-data")); } } 

在DotNet Framework 4.5.2的MVC 5中,我们在“Accept”头文件中得到了“application / json,plaint text ..”这将很好用,如下所示:

 isJson = headers["Content-Type"] == "application/json" || headers["Accept"].IndexOf("application/json", System.StringComparison.CurrentCultureIgnoreCase) >= 0;