为什么 Proxy 认证要发两次请求
Contents
现象
这段时间通过上服务器观察日志,发现部分语言的 HTTP 客户端在进行代理认证的时候会发送两次 HTTP 请求
- 第一次请求不会携带任何认证信息
- 第二次请求才会携带上
Proxy-Authorization
的 Header
涉及到的 HTTP 客户端还是很多的,例如:
- Java 的 httpclient、jsoup
- C# 的 HTTP 客户端
- Chrome 使用 SwitchyOmega 插件
原因
Go 的 net 包和 Python 的 Request 就没有这个问题,虽然在用户侧发送两次请求是无感知的,但是一个请求发送两次,相应耗时还是会增加的
由于对 Java 等不太熟悉,就不直接看源码了,google 找了下问题原因:
直接破案了,因为 HTTP 协议中的 Proxy-Authenticate
Header,当请求中没有 Proxy-Authorization
时,它需要伴随着 407 (Proxy Authentication Required)
一并返回给客户端,告诉客户端使用那种认证方式
最常见的就是 Basic 认证(用户名: 密码计算 base64):
|
|
除了 Basic,还有如下认证方案 :
- Basic (查看 RFC 7617 ,base64 编码凭证。详情请参阅下文.),
- Bearer (查看 RFC 6750 ,bearer 令牌通过 OAuth 2.0 保护资源),
- Digest (查看 RFC 7616 ,只有 md5 散列 在 Firefox 中支持,查看 bug 472823 用于 SHA 加密支持),
- HOBA (查看 RFC 7486 (草案),HTTP Origin-Bound 认证,基于数字签名),
- Mutual (查看 draft-ietf-httpauth-mutual ),
- AWS4-HMAC-SHA256 (查看 AWS docs )
后来进行测试,当 Proxy 返回 407 不带 Proxy-Authenticate: Basic
,这种发送两次的 HTTP 客户端就不会在发送第二次请求了(当然用户收到的请求状态码也是 407)…
回到最初的问题,发送两次的原因就是
- 第一次要拿到
Proxy-Authenticate
,Proxy 是通过什么方式认证的,如 Basic - 通过返回的认证方式使用对应的算法对认证信息进行编码
改进
发送两次的 HTTP 客户端更多的考虑到通用性,用户可以在不知道 Proxy 使用什么认证方式的情况下正常使用
开发者在实际项目使用中,考虑到性能因素,在知道认证方式的情况下:
- 不使用 HTTP 框架提供的认证写法,直接把
Proxy-Authorization
添加到 Header 中 - 使用白名单 IP 认证
对于那些每次发送两次的 HTTP 框架也有优化方向
- 能自主选择认证方式,像 Go、Python 这类的认证方式默认就是 Basic
- 对于地址相同的 Proxy,第一次拿到认证方式后进行缓存,后续就不必在每次请求发送两次