C# HttpListener 多种身份验证方案和 Chrome

vas*_*sib 5 c# google-chrome http httplistener www-authenticate

好吧,这是一个很长的问题,但我认为这是值得的。我们有什么:

  1. 一个示例虚拟 C# 控制台应用程序,启动自托管 ASP.Net WebAPI 服务(Microsoft.AspNet.WebApi.OwinSelfHostNuGet 包):

    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();
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 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);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 具有两个公共方法的示例 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 };
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

现在,当我们运行项目并将 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"}
Run Code Online (Sandbox Code Playgroud)

但是当我们访问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."}
Run Code Online (Sandbox Code Playgroud)

除了一件事之外,这几乎“符合预期”。我希望当我的浏览器收到带有www-authenticate标头的 401 HTTP 响应时,他将尝试使用指定的身份验证重复相同的请求(如果请求中没有任何其他授权标头)。但出于某种原因他没有:(

为了让事情变得更有趣,我们可以看到这AuthenticationSchemes.IntegratedWindowsAuthentication实际上是AuthenticationSchemes.Negotiate | AuthenticationSchemes.Ntlm这样,当我删除其中一个时,事情开始按预期工作!例如:在我们的 Startup 类中替换IntegratedWindowsAuthenticationNegotiate并将您的浏览器转到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"}
Run Code Online (Sandbox Code Playgroud)

一般来说,我们的服务器首先响应 401 HTTP 状态并设置标头www-authenticate: Negotiate,然后浏览器使用附加的授权标头重复请求。如果我们替换IntegratedWindowsAuthentication为 ,同样的情况Ntlm

还有一个例子可以让事情变得更清楚。如果我们AuthenticationSchemes.Anonymous只删除并保留,AuthenticationSchemes.IntegratedWindowsAuthentication我们会注意到结果中的两件事:

  1. /sample/public端点不再可用于匿名请求(如预期)
  2. /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
Run Code Online (Sandbox Code Playgroud)

所以,我的问题是:将多个身份验证方案放在单个标头中是否“可以” www-authenticate?如果“是的,没关系”,为什么我的 Chrome 不处理这种情况?如果“不!这完全是错误的!”,为什么HttpListener要这样做?我怎样才能绕过这个问题?请帮忙!

Ali*_*c W 2

不久前就有人问过这个问题,但我想提供我的 .02。AuthenticationSchemes.IntegratedWindowsAuthentication 有点奇怪。IWA 与设置 Negotiate和NTLM相同。另一方面,如果 Kerberos 失败,协商将退回到 NTLM。ASP.NET Core 枚举中没有 IWA:请参阅 ASP.NET Core AuthenticationSchemes Enum

我用:

listener.AuthenticationSchemes = AuthenticationSchemes.Anonymous | AuthenticationSchemes.Negotiate;
Run Code Online (Sandbox Code Playgroud)