在浏览器中,由于 HTTP 和 NTLM 身份验证协议的性质,您无法避免这种情况。
但是,我不认为整个文件的数据实际上被发送了两次。
我将以下内容发送到 IIS 7.5 上受 NTLM 保护的文件:
POST /test/upload HTTP/1.1
Host: localhost
Content-Length: 5
请注意,我实际上并没有发送任何内容,只发送了完整的标题。然而,IIS立即回应:
HTTP/1.1 401 Unauthorized
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/7.5
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
Date: Thu, 10 Jan 2013 05:17:58 GMT
Content-Length: 6277
...
这意味着如果我是一个上传大文件的浏览器,在我传输很多字节之前我就会被切断。我假设(希望)任何表现良好的浏览器都会放弃文件上传并开始 HTTP 身份验证。
也许值得你花时间去看看像 Wireshark 这样的东西,看看实际发生了什么。 (记得用不同的浏览器进行测试!)不要相信浏览器内的开发者工具,也不要使用像 Fiddler 这样的调试代理。 (事实上,如果您使用的是 Fiddler,这实际上可能是问题所在:它会在将 anything 发送到服务器之前缓冲 整个 请求。)
如果您确实发现整个文件确实被完整上传了两次(也许旧版本的 IIS 没有立即发送 401?),请考虑一下事情必须如何发生:
-
用户提交上传,导致浏览器向服务器发出POST 请求。因为浏览器不可能知道 URL 是否需要认证,它只是发送 complete 请求。
POST /upload HTTP/1.1
Content-Type: mutlipart/form-data
Content-Length: 1024
...
服务器可能会立即响应 401,但行为不佳的用户代理会传输整个请求。
-
一旦请求完成并且浏览器看到401,我们就会开始NTLM dance。客户端必须发出带有Type 1 消息的新请求,服务器以另一个401 响应,这次包含Type 2 消息,最后客户端发出带有Type 3 的第三个请求包含实际凭据的消息。
由于客户端知道第二个请求在 NTLM 协商中保证是另一个401,它知道它可以跳过实体(表单数据)的传输。但是,它必须在第三次请求时再次发送 post 数据,因为我们必须假设服务器丢弃了原始请求数据。
我们的文件终于上传了! (但它必须传输两次。)
因此,虽然我认为您不会真正看到 NTLM 身份验证的完整双重上传问题,但您可能仍希望消除这种可能性。以下是我的处理方法:
将上传 URL 移出 NTLM 认证区域。在上传页面(经过 NTLM 身份验证)上,使用会话 cookie(在加载页面时设置)或发出令牌 - 隐藏字段中的 GUID 就足够了。
接收上传的脚本可以拒绝任何没有有效会话和/或令牌的请求。
如果您使用 NTLM 凭据作为授予对 NTFS 资源的访问权限的机制,则可以将上传文件写入匿名可写临时文件夹,完成后,将请求重定向到第二个受 NTLM 保护的脚本,该脚本会移动文件到它的目的地。