好的,这是一个很长的问题,但我认为这是值得的。我们有什么:
一个示例虚拟 C# 控制台应用程序,它启动自托管 owin ASP.Net WebAPI 服务(
Microsoft.AspNet.WebApi.OwinSelfHost
NuGet 包):class Program { static void Main(string[] args) { var url = "http://localhost:8080/"; Console.WriteLine($"Starting on {url}"); using (WebApp.Start<Startup>(url)) { Console.WriteLine("Success! Press any key to stop..."); Console.ReadKey(); } } }
OWIN启动类:
class Startup { public void Configuration(IAppBuilder app) { // enable Windows AND Anonymous authentication var listener = app.Properties["System.Net.HttpListener"] as HttpListener; listener.AuthenticationSchemes = AuthenticationSchemes.Anonymous | AuthenticationSchemes.IntegratedWindowsAuthentication; // configure WebAPI var config = new HttpConfiguration(); config.MapHttpAttributeRoutes(); app.UseWebApi(config); } }
具有两个公共方法的示例 WebAPI 控制器:
[RoutePrefix("sample"), Authorize] public class SampleController : ApiController { [Route("public"), AllowAnonymous] public object GetPublicSample() { var message = $"Hi there, mr. {User?.Identity?.Name ?? "ANONYMOUS"}"; return new { sample = 0, message }; } [Route("protected")] public object GetProtectedSample() { var message = $"Hi there, mr. {User?.Identity?.Name ?? "ANONYMOUS"}"; return new { sample = 42, message }; } }
现在,当我们运行我们的项目并将 chrome 指向http://localhost:8080/sample/public时,会调用此请求:
GET /sample/public HTTP/1.1
Host: localhost:8080
HTTP/1.1 200 OK
Content-Length: 50
Content-Type: application/json; charset=utf-8
Server: Microsoft-HTTPAPI/2.0
Date: Wed, 28 Feb 2018 08:05:56 GMT
{"sample":0,"message":"Hi there, mr. ANONYMOUS"}
但是当我们访问http://localhost:8080/sample/protected时,我们会看到:
GET /sample/protected HTTP/1.1
Host: localhost:8080
HTTP/1.1 401 Unauthorized
date: Wed, 28 Feb 2018 08:19:01 GMT
www-authenticate: Negotiate,NTLM
server: Microsoft-HTTPAPI/2.0
content-length: 61
content-type: application/json; charset=utf-8
{"Message":"Authorization has been denied for this request."}
这几乎是“预期的”,除了一件事。我希望当我的浏览器收到带有www-authenticate
标头的 401 HTTP 响应时,他将尝试使用指定的身份验证重复相同的请求(如果请求中没有任何其他 Authorization 标头)。但他不这样做,出于某种原因:(
为了让事情更有趣,我们可以看到实际上AuthenticationSchemes.IntegratedWindowsAuthentication
是这样AuthenticationSchemes.Negotiate | AuthenticationSchemes.Ntlm
,当我删除其中一个时,事情开始按预期工作!例如:在我们的 Startup 类中替换IntegratedWindowsAuthentication
为Negotiate
并将您的 brouser 带到http://localhost:8080/sample/protected
GET /sample/protected HTTP/1.1
Host: localhost:8080
HTTP/1.1 200 OK
date: Wed, 28 Feb 2018 08:29:55 GMT
www-authenticate: tOkeN1231351234153=
server: Microsoft-HTTPAPI/2.0
content-length: 59
content-type: application/json; charset=utf-8
{"sample":42,"message":"Hi there, mr. DOMAIN\\username"}
通常,我们的服务器首先响应 401 HTTP 状态并设置标头www-authenticate: Negotiate
,然后浏览器使用附加的授权标头重复请求。如果我们替换IntegratedWindowsAuthentication
为Ntlm
.
还有一个让事情更清楚的例子。如果我们AuthenticationSchemes.Anonymous
只删除并离开,AuthenticationSchemes.IntegratedWindowsAuthentication
我们会注意到两件事:
/sample/public
端点不再可用于匿名请求(如预期的那样)/sample/protected
端点现在可以正常工作(!)
如果我们查看第一个 401 服务器响应,我们会注意到有两个 www-authenticate 标头而不是一个(和以前一样):
GET /sample/protected HTTP/1.1
Host: localhost:8080
HTTP/1.1 401 Unauthorized
Content-Length: 0
Server: Microsoft-HTTPAPI/2.0
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
Date: Wed, 28 Feb 2018 08:44:04 GMT
所以,我的问题是:将多个身份验证方案放在单个www-authenticate
标头中是否“可以”?如果“是的,没关系”,为什么我的 chrome 不处理这种情况?如果“不!完全错了!”,为什么HttpListener
要这样做,我该如何绕过呢?请帮忙!