Web Api 跨域基础认证
Posted
技术标签:
【中文标题】Web Api 跨域基础认证【英文标题】:Web Api Cross Domain Basic Authentication 【发布时间】:2013-06-06 14:08:30 【问题描述】:我已经设置了一个 web api 以允许使用基本身份验证进行跨域访问。当我向 API 发出跨域 GET 请求时,它工作正常,并且我在自定义消息处理程序的“授权”标头中获取令牌。但是在发起跨域 POST 请求时,我没有收到“授权”标头,这就是无法验证请求的原因。
任何帮助将不胜感激。
以下是用于跨域访问的自定义消息处理程序的代码。
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace MyWebApi.Handlers
public class XHttpMethodOverrideDelegatingHandler : DelegatingHandler
static readonly string[] HttpOverrideMethods = "PUT", "DELETE" ;
static readonly string[] AccessControlAllowMethods = "POST", "PUT", "DELETE" ;
private const string HttpMethodOverrideHeader = "X-HTTP-Method-Override";
private const string OriginHeader = "ORIGIN";
private const string AccessControlAllowOriginHeader = "Access-Control-Allow-Origin";
private const string AccessControlAllowMethodsHeader = "Access-Control-Allow-Methods";
private const string AccessControlAllowHeadersHeader = "Access-Control-Allow-Headers";
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
var authHeader = request.Headers.Authorization;
if (authHeader == null || authHeader.Scheme != "Basic" || string.IsNullOrWhiteSpace(authHeader.Parameter))
return CreateUnauthorizedResponse();
if (request.Method == HttpMethod.Post && request.Headers.Contains(HttpMethodOverrideHeader))
var httpMethod = request.Headers.GetValues(HttpMethodOverrideHeader).FirstOrDefault();
if (HttpOverrideMethods.Contains(httpMethod, StringComparer.InvariantCultureIgnoreCase))
request.Method = new HttpMethod(httpMethod);
var httpResponseMessage = base.SendAsync(request, cancellationToken);
if (request.Method == HttpMethod.Options && request.Headers.Contains(OriginHeader))
httpResponseMessage.Result.Headers.Add(AccessControlAllowOriginHeader, request.Headers.GetValues(OriginHeader).FirstOrDefault());
httpResponseMessage.Result.Headers.Add(AccessControlAllowMethodsHeader, String.Join(", ", AccessControlAllowMethods));
httpResponseMessage.Result.Headers.Add(AccessControlAllowHeadersHeader, HttpMethodOverrideHeader);
httpResponseMessage.Result.StatusCode = HttpStatusCode.OK;
//No mater what the HttpMethod (POST, PUT, DELETE), if a Origin Header exists, we need to take care of it
else if (request.Headers.Contains(OriginHeader))
httpResponseMessage.Result.Headers.Add(AccessControlAllowOriginHeader, request.Headers.GetValues(OriginHeader).FirstOrDefault());
return httpResponseMessage;
private Task<HttpResponseMessage> CreateUnauthorizedResponse()
var response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
response.Headers.Add("WWW-Authenticate", "Basic");
var taskCompletionSource = new TaskCompletionSource<HttpResponseMessage>();
taskCompletionSource.SetResult(response);
return taskCompletionSource.Task;
我已经在 Application_Start 中注册了上述处理程序,如下所示:
namespace MyWebApi
public class Global : System.Web.HttpApplication
protected void Application_Start(object sender, EventArgs e)
RouteTable.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/controller/action/id",
defaults: new id = RouteParameter.Optional);
GlobalConfiguration.Configuration.MessageHandlers.Add(new XHttpMethodOverrideDelegatingHandler());
GlobalConfiguration.Configuration.Formatters.Insert(0, new JsonpMediaTypeFormatter());
在不同域项目的客户端,我正在尝试使用以下代码添加新记录。
AddUser
var jsonData =
"FirstName":"My First Name",
"LastName": "My Last Name",
"Email": "my.name@mydomain.com",
"Password": "MyPa$$word"
;
$.ajax(
type: "POST",
dataType: 'json',
url: "http://localhost:4655/api/user/signup",
beforeSend: function (xhr) xhr.setRequestHeader("Authorization", "Basic xxxxxxxxxxxxxx"); ,
accept: "application/json",
data: JSON.stringify(jsonData),
success: function (data)
alert("success");
,
failure: function (errorMsg)
alert(errorMsg);
,
error: function (onErrorMsg)
alert(onErrorMsg.statusText);
,
statusCode: function (test)
alert("status");
);
);
以下是我的用户控制器的代码。
namespace MyWebApi.Controllers
public class UserController : ApiController
[HttpPost]
[ActionName("Adduser")]
public int Post(UserModel source)
if (source == null)
throw new ArgumentNullException("source");
Db.Users.Add(source);
Db.SaveChanges();
return source.UserId;
提前致谢!
【问题讨论】:
我也在尝试连接 Web API REST 服务以允许基本授权并允许 CORS 请求(GET 和 POST)。根据下面的文章,我想知道在生产中使用此代码是否被认为是最佳实践。你有没有设法让这个工作? blog.bittercoder.com/2012/09/09/cors-and-webapi 很遗憾没有。我没有找到让它工作的方法。而且由于复杂性和浏览器依赖性,我已将其删除为启用 CORS 的 WEB API。 【参考方案1】:我发现,如果我在我的跨域 (POST) XHR 请求中包含基本身份验证凭据,浏览器(IE、Chrome、Firefox)会在请求到达我的服务器之前拒绝该请求 - 这甚至是真的如果我在初始 $.ajax() 请求中指定 withCredentials:true
。我猜 CORS 规范中可能有一些东西需要这个。但我认为简短的回答是您不能在 CORS 请求中指定基本身份验证。
当然,你可以通过其他方式解决这个问题,通过将用户 ID 和密码作为 URL 的一部分传递,所以我不完全清楚他们认为通过限制它可以获得什么,但大概他们有一些原因。
【讨论】:
你发现了吗,我想可能是服务器不理解 preFlight OPTIONS 动词。使用 OPTIONS 作为动词发出请求,服务器会抛出 404,这就是我的应用程序中发生的情况。我认为这不是浏览器问题。 @TyroneMichael - 这里有相当多的黑魔法,所以我没有声称已经明确地弄清楚了任何事情:-)。但我能够让我的服务器理解并正确响应预检 OPTIONS 动词,所以我不认为问题出在服务器端。已经有几个星期了,但正如我从测试中回忆的那样,我看到的行为的唯一解释是浏览器使用基本身份验证阻止了我的 XHR 请求。我很想错 - 这是我想工作的场景。 你整天都在试验。安装了每晚的 Web-Api 构建,似乎已经偏离正题。试图走这条路线link。但是,当浏览器尝试使用基本身份验证进行飞行前请求时,仍然会得到 404。如果我不设置 Auth 标头,一切似乎都可以正常工作并且我的断点被命中。 @TyroneMichael - 是的,这正是我遇到的问题。这似乎表明它不是一个受支持的场景,我需要想出一些其他的方式来进行身份验证——大概只是通过在 URL 或 POST 中传递参数。 我得到了这个工作。不知道它是否会帮助你,但我正在装饰我的控制器,比如[HttpGet("dealerships/id/auctions")]
。如果我同时添加了HttpGet("dealerships/id/auctions")]
和HttpOptions("dealerships/id/auctions")]
,问题就解决了【参考方案2】:
你需要用 [HttpOptions] 和 [HttpPost] 来装饰你的控制器。否则,当它使用 OPTIONS 动词发出请求时,它会抛出 404。所以你的控制器将是
[HttpPost]
[HttpOptions]
[ActionName("Adduser")]
public int Post(UserModel source)
if (source == null)
throw new ArgumentNullException("source");
Db.Users.Add(source);
Db.SaveChanges();
return source.UserId;
【讨论】:
以上是关于Web Api 跨域基础认证的主要内容,如果未能解决你的问题,请参考以下文章
Office 365 OAuth2登录认证如何实现跨域请求?
PHP驱动的API如何认证真正的客户端(referer)跨域(知道标头可以被欺骗)?
以短链服务为例,探讨免AppKey免认证Ajax跨域调用新浪微博API