2

我将非常感谢任何可能帮助我解决问题的帮助。

从 Excel VBA 代码中,我需要从 HTTPS 站点https://redmine.itransition.com/下载和解析 CSV 文件。我尝试使用 WinHTTP 来获取文件。但是,我不明白为什么身份验证不起作用。这是一段相关代码:

TargetURL = "https://redmine.itransition.com/projects/pmct/time_entries.csv"
Set HTTPReq = CreateObject("WinHttp.WinHttpRequest.5.1")
HTTPReq.Option(4) = 13056 ' WinHttpRequestOption_SslErrorIgnoreFlags 13056: ignore all err, 0: accept no err
HTTPReq.Open "GET", TargetURL, False
HTTPReq.SetCredentials "UN", "PW", 0
HTTPReq.send

返回以下响应(仅列出某些字符串):

Content-Type: text/html; charset=utf-8
Status: 406
X-Runtime: 5

但是,如果我在成功使用手动身份验证后从 Firefox cookie 发送“Cookie”字符串

HTTPReq.setRequestHeader "Cookie", SetCookieString
HTTPReq.send

我很容易得到预期的文件。当然我对这样的解决方案不满意,并希望执行真正的 WinHTTP 身份验证。但是,我无法理解代码中有什么问题或遗漏了什么。很可能我必须使用.SetClientCertificate方法,但这对我来说不清楚 - 需要哪个证书?

或者,更笼统地说:我应该使用哪些 WinHTTP 方法或函数进行调试,以找出哪个步骤阻塞/不正确并阻止我进行正确的身份验证?我花了 2 周时间通过 MSDN 和各种资源寻找,但仍然没有解决方案。

提前感谢您的建议!

4

3 回答 3

6

上面的@Alex K. 回复正是我一直在寻找的!在 Firebug 和 MSDN 的帮助下,我完成了 3 个请求:

  • 使用 RegEx 从登录页面收集authentity_token 数据的 GET 请求
  • POST 请求以进行身份​​验证并从响应中收集所需的 Cookie 字符串
  • GET 请求最终获得我心爱的 CSV

以下代码按预期工作:

Set RegX_AuthToken = CreateObject("VBScript.RegExp")
' Below Pattern w/o double-quotes encoded: (?:input name="authenticity_token" type="hidden" value=")(.*)(?:")
RegX_AuthToken.Pattern = "(?:input name=" & Chr(34) & "authenticity_token" & Chr(34) & " type=" & Chr(34) & "hidden" & Chr(34) & " value=" & Chr(34) & ")(.*)(?:" & Chr(34) & ")"
RegX_AuthToken.IgnoreCase = True
RegX_AuthToken.Global = True

TargetURL = "https://redmine.itransition.com/login"

Set HTTPReq = CreateObject("WinHttp.WinHttpRequest.5.1")
HTTPReq.Open "GET", TargetURL, False
HTTPReq.Send

Set Token_Match = RegX_AuthToken.Execute(HTTPReq.ResponseText)
AuthToken = Token_Match.Item(0).SubMatches.Item(0)

PostData = "authenticity_token=" & AuthToken & "&back_url=https://redmine.itransition.com/" & "&username=" & UN & "&password=" & PW & "&login=Login »"

HTTPReq.Open "POST", TargetURL, False
HTTPReq.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
HTTPReq.Send (PostData)

SetCookieString = HTTPReq.GetResponseHeader("Set-Cookie")

TargetURL = "https://redmine.itransition.com/projects/pmct/time_entries.csv"
HTTPReq.Open "GET", TargetURL, False
HTTPReq.setRequestHeader "Cookie", SetCookieString
HTTPReq.Send

以下 URL 有助于构建 POST 请求:http ://tkang.blogspot.com/2010/09/sending-http-post-request-with-vba.html

您需要在没有凭据的情况下加载该页面,从生成的表单中获取看起来像 volatile 字段authentity_token 的内容,并将其连同用户名和密码一起发布到/login。

Alex K. - 再次感谢您的精彩建议!(:

于 2013-01-08T19:28:15.160 回答
3

https://redmine.itransition.com/上的登录只是一个 HTML 表单,它将用户名和密码发布到/login.

SetCredentials这与为基于服务器的身份验证方案(如基本/摘要/ntlm)设计的不兼容。

您需要在没有凭据的情况下加载该页面,从生成的表单中获取看起来像 volatile 字段的内容authenticity_token,并将其连同用户名和密码一起发布到/login.

如果它是一个基于会话的系统,它将使用您需要在后续请求中使用的 set-cookie 标头 + 数据进行响应。

于 2013-01-07T11:36:29.570 回答
0

还有一件事(除了上述解决方案)在使用类似于上述代码的 POST 请求的情况下应该注意:由于某种模糊的原因,我仍然在 4-5 次(甚至更多)中得到一次讨厌的 406来自网站的响应,这意味着在我的情况下auth is NOT complete。经过几个小时的逐步调试,我很高兴地找到了原因:auth token 值可能有+符号,并且通过分析数十个 auth token / 响应代码的箭头,我发现包含 -+的令牌与 406 代码完全匹配。

解决方案变得非常明显:+PostData. 在http://www.blooberry.com/indexdot/html/topics/urlencoding.htm的帮助下,我终于得出以下结论:

PostData = "authenticity_token=" & Replace(AuthToken, "+", "%2B", vbTextCompare) & _
    "&back_url=https://redmine.itransition.com/projects/" & Trim(RedmineProject) & _
    "/time_entries" & "&username=" & UN & "&password=" & PW & "&login=Login »"

+es 被%2Bs 取代,这就是 - 不再是 406s!)

在我的情况下,其他特殊字符无关紧要,但吸取了教训。希望这将为其他人节省几个小时的生命!

于 2013-01-10T09:11:53.133 回答