我正在编写一个HTTP API包装器来集成特定的IP摄像头.部分过程需要URI中的登录凭据,例如:
http://user:pass_with_#_sign@address:port/somespecificpath/
我正在使用Indy TIdHTTP.Get
将请求发送到设备.但是,密码由"#"字符组成.如果此URI放在任何具有普通密码的浏览器中,则该#
字符会将其抛出.因此,我需要将密码编码#
为%23
...
http://user:pass_with_%23_sign@address:port/somespecificpath/
当我将此URI粘贴到任何浏览器中时,它会成功登录并执行所需的操作.但是,当我传入完全相同的URI时TIdHTTP.Get
,它无法成功登录,因此只要密码包含#
(或%23
),我就无法做任何事情.将密码更改为不包含#
一个解决方案太过于草率.Indy必须用这个URI /密码弄乱一些东西.
这是Indy中的一个错误,还是我需要做些什么来让Indy接受这样的编码密码?
UPDATE
我在其中一个摄像头上添加了一个用户名和密码的新帐户,没有任何需要编码的特殊字符,并且身份验证仍然无效.好像Indy正在从URI中完全剥离登录凭据,甚至不发送这些凭据.接下来我需要做的是监视实际发送的URI.
更新2
我通过WireShark进行了数据包捕获,并验证了凭证...user:pass@...
甚至没有被发送 - 它们被从Indy实际发送的URI中删除.
更新3
TLama建议我捕获使用浏览器时发送的内容.我试过这个,当然即使使用浏览器,捕获也不会显示这些登录凭据......即使它可以在浏览器中运行.因此,我必须找出另一种方法来确定是否发送了这些凭据.
更新4
我尝试(在看到Remy的回答之前)在URI中提供这些凭据,Request.Username
而Request.Password
不是在URI中,我仍然没有成功.我一直从设备上取回"未经授权".
更新5
此API的文档提到与本段以外的用户身份验证无关的任何内容:
Grandstream Video Surveillance API(应用程序编程接口)支持HTTP 1.0协议(RFC1945).本文档通过支持的GET/POST方法详细说明了客户端功能的参数.用户需要管理员权限才能检索或设置参数.
在那方面,我确实将TIdHTTP
协议版本切换为1.0
.
更新6
希望最后一次更新需要......我对浏览器(Chrome)和浏览器之间的数据包捕获进行了另一次比较TIdHTTP
.铬实际上发送两个请求,第一个不具有任何凭证,但在第二个请求有一个在首部的节点Authorization: Basic...
和Credentials: User:Pass
,而使用TIdHTTP
只发送在没有这些凭证1个单个请求.
更新7
7是一个幸运数字:-)我刚刚意识到,我对设备的第一个请求返回"未授权",但我所做的所有后续请求(使用相同的TIdHTTP
实例)都是成功的!所以回到我之前的更新,就像我在浏览器捕获中看到的那样,它需要第二次重复请求才能工作.
#
在URL的片段部分之前的URL中是非法字符,这就是为什么它必须%23
在URL的其他区域中使用时进行编码的原因.
用户名/密码实际上不是真实URL的一部分,这就是为什么在TIdHTTP
将URL发送到服务器时被剥离的原因(监视任何Web浏览器的流量,您将看到同样的事情发生).
要使用HTTP身份验证TIdHTTP
,您需要使用TIdHTTP.Request.Username
和TIdHTTP.Request.Password
属性(并且您不需要对值进行URL编码),例如:
IdHTTP1.Request.Username := 'user'; IdHTTP1.Request.Password := 'pass_with_#_sign'; IdHTTP1.Get('http://address:port/somespecificpath/');
如果您传递的URL中包含已编码的用户名/密码,TIdHTTP
则会删除这些值并将其移动到Request.Username
和Request.Password
您的属性,但它们将保持原始编码格式,例如:
IdHTTP1.Get('http://user:pass_with_%23_sign@address:port/somespecificpath/') // this will set Request.Username to 'user', // Request.Password to 'pass_with_%23_sign', and // send a request for 'http://address:port/somespecificpath/'
如果您有一个编码的URL开头,您可以使用TIdURI
该类在调用之前手动解码它TIdHTTP.Get()
,例如:
var RequestUrl: string; Uri: TIdURI; begin RequestUrl := 'http://user:pass_with_%23_sign@address:port/somespecificpath/'; ... Uri := TIdURI.Create(RequestURL); try IdHTTP1.Request.Username := TIdURI.URLDecode(Uri.UserName); IdHTTP1.Request.Password := TIdURI.URLDecode(Uri.Password); RequestURL := Uri.URI; finally Uri.Free; end; IdHTTP1.Get(RequestUrl); ... end;
更新:无论哪种方式,请确保您的子句中有适当的IdAuthentication...
单位或IdAllAuthentications
单位,uses
以启用Indy的HTTP身份验证类TIdHTTP
.