使用Invoke-WebRequest命令和用户名、密码进行基本身份验证,在GitHub API上进行操作。

190
使用 cURL,我们可以如下方式在 HTTP web 请求中传递用户名:
$ curl -u <your_username> https://api.github.com/user

-u标志接受用户名进行身份验证,然后cURL将请求密码。该cURL示例适用于使用GitHub Api进行基本身份验证

我们如何类似地通过Invoke-WebRequest传递用户名和密码?最终目标是在GitHub API中使用PowerShell进行基本身份验证。


$pair 应该是 $pair = "$($user):$($pass)"。请查看已批准的答案。我之前使用上述代码时遇到了很多问题。 - Bhavjot
没有任何建议使用-Credential方法的解决方案能够正常工作,因为在发出请求时并没有生成正确的身份验证标头。 - StingyJack
@Shaun Luttin - 这是一个问题和答案网站,而不是一个问题回答网站。这位用户更喜欢看到简洁的问题和答案,而不是针对特定情况的解决方案,但不必重复阅读(一次在编辑后的问题中,现在是QuestionAnswer,然后再在答案中)。如果担心帮助您的答案不会最接近问题,StackExchange具有将最佳/接受的答案尽可能靠近问题的功能。 - user66001
1
@user66001 感谢您的反馈。我已将我的回答问题移动到自己的答案中以供日后参考。我认为这是一个改进。 - Shaun Luttin
@ShaunLuttin - 好主意! :) - user66001
curl 中,我必须使用参数 --anyauth 才能使用用户名/密码。否则会出现权限错误。 - Timo
9个回答

237

我在这里假设使用基本身份验证。

$cred = Get-Credential
Invoke-WebRequest -Uri 'https://whatever' -Credential $cred

您可以通过其他方式(Import-Clixml等)获取凭据,但它必须是一个[PSCredential]对象。

基于评论进行编辑:

Github正在违反RFC规范,如他们在您提供的链接中所解释的:

API支持基本认证(Basic Authentication),如RFC2617所定义的,但有一些细微差别。主要区别是RFC要求未经身份验证的请求应以401未授权响应进行回答。在许多地方,这将披露用户数据的存在。相反,GitHub API响应为404未找到。这可能会对假定401未经授权响应的HTTP库造成问题。解决方法是手动制作Authorization标头。

就我所知,Powershell的Invoke-WebRequest会等待401响应再发送凭据,而由于GitHub从未提供401响应,因此您的凭据永远不会被发送。

手动构建标头

相反,您需要自己创建基本认证标头。

基本认证接受一个字符串,该字符串由用户名和密码用冒号user:pass分隔,然后发送其Base64编码结果。

像这样的代码应该可以工作:

$user = 'user'
$pass = 'pass'

$pair = "$($user):$($pass)"

$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair))

$basicAuthValue = "Basic $encodedCreds"

$Headers = @{
    Authorization = $basicAuthValue
}

Invoke-WebRequest -Uri 'https://whatever' -Headers $Headers
你可以将一些字符串连接起来,但我想将其拆开以使其更清晰。

1
就像我说的,它适用于基本身份验证,但我不知道GitHub API使用什么类型的身份验证。您可以发布一些有关预期内容的详细信息,这可能有助于我们解决问题。 - briantist
1
啊,看起来GitHub(据他们自己承认)没有遵循RFC标准,但PowerShell是的。我已经编辑了答案,提供了更多信息和解决方法。 - briantist
1
@Aref,你应该发布一个新的问题,并附上你正在使用的代码。如果你这样做并让我知道,我会帮你看一下。 - briantist
2
如果您尝试对Visual Studio Team Services REST API进行身份验证,那么您需要手动构建标头。 - Brent Robinson
1
似乎Bitbucket API版本2.0也需要手动创建授权头。 - Florian Winter
显示剩余6条评论

67

使用这个:

$root = 'REST_SERVICE_URL'
$user = "user"
$pass= "password"
$secpasswd = ConvertTo-SecureString $pass -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($user, $secpasswd)

$result = Invoke-RestMethod $root -Credential $credential

由于某些原因,当我在TFS vNext上使用所选答案时,它对我没有起作用,但是这个答案却解决了我的问题。非常感谢! - Tybs
所选答案无法在Azure上运行PowerShell Runbook以启动触发的作业,但这个答案可以。 - Sam
2
对我没用。根据Fiddler,没有添加标题。 - NickG
我可以确认这种方法在macOS上的PowerShell Core 7.3.2上运行良好。 - Van Vangor

38

如果有人需要一句话简述:

iwr -Uri 'https://api.github.com/user' -Headers @{ Authorization = "Basic "+ [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes("user:pass")) }

11

Invoke-WebRequest 遵循RFC2617,如 @briantist所指出的那样,但是有一些系统(例如JFrog Artifactory)允许匿名使用,如果Authorization头不存在,则会响应,但是如果头包含无效凭据,则会响应401 Forbidden

这可以用来触发401 Forbidden响应并使 -Credentials工作。

$login = Get-Credential -Message "Enter Credentials for Artifactory"

                              #Basic foo:bar
$headers = @{ Authorization = "Basic Zm9vOmJhcg==" }  

Invoke-WebRequest -Credential $login -Headers $headers -Uri "..."

第一次发送的是无效标头,因为-Credentials会覆盖Authorization标头,所以在第二个请求中会用有效凭据替换。

已在 Powershell 5.1 中测试。


如果使用credential提供用户和密码,那么基本授权头是否必要?根据您的回答,-credential只有在从headers获得401之后才能工作,因此必须有basic foo:bar以避免匿名。 - Timo
1
是的,正如我在答案中所写的,如果允许混合访问,则需要向“Authorization”标头添加无效凭据以触发“401”,然后才能使“-Credential”起作用。 - Leonard Brünings
所以如果 foo:bar 是正确的,我需要将其操作为例如 foobar:bar 用于 auth header key - Timo
当然,如果有人选择了非常安全的用户名和密码 foo:bar,那么你需要使用其他的东西。 - Leonard Brünings

10

我必须这样做才能使它工作:

$pair = "$($user):$($pass)"
$encodedCredentials = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($Pair))
$headers = @{ Authorization = "Basic $encodedCredentials" }
Invoke-WebRequest -Uri $url -Method Get -Headers $headers -OutFile Config.html

7

这里有另一种使用WebRequest的方法,我希望它能对你有用。

$user = 'whatever'
$pass = 'whatever'
$secpasswd = ConvertTo-SecureString $pass -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($user, $secpasswd)
$headers = @{ Authorization = "Basic Zm9vOmJhcg==" }  
Invoke-WebRequest -Credential $credential -Headers $headers -Uri "https://dc01.test.local/"

以防有人想知道,-Headers 参数需要一个 [string],[string] 的字典或哈希表。实际上你可以直接定义一个数组 - 例如 @(Authorization = 'Basic ...') 像他在这里所做的一样,PowerShell 将为您进行类型转换。 - Kellen Stuart
@KellenStuart 不要将 basicdigest 混淆,这个答案中使用了 credential 参数。 - Timo

5
这是适用于我们特定情况的解决方案。
备注来自客户端基础认证的维基百科页面。感谢@briantist的答案提供的帮助!
将用户名和密码组合成单个字符串username:password
$user = "shaunluttin"
$pass = "super-strong-alpha-numeric-symbolic-long-password"
$pair = "${user}:${pass}"

将字符串编码为 RFC2045-MIME 变体的 Base64,但不限制于每行 76 个字符。

$bytes = [System.Text.Encoding]::ASCII.GetBytes($pair)
$base64 = [System.Convert]::ToBase64String($bytes)

将Auth值创建为方法、空格,然后编码对应的Method Base64String
$basicAuthValue = "Basic $base64"

创建头信息Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
$headers = @{ Authorization = $basicAuthValue }

调用Web请求

Invoke-WebRequest -uri "https://api.github.com/user" -Headers $headers

这里的PowerShell版本比cURL版本冗长。为什么呢?@briantist指出,GitHub正在违反RFC规范,而PowerShell则坚持遵循该规范。这是否意味着cURL也与标准不符?

3

我知道这有点偏离原始请求,但我在寻找一种使用Invoke-WebRequest对需要基本身份验证的站点进行操作的方法时遇到了这个问题。

区别在于,我不想在脚本中记录密码。相反,我想提示脚本运行者为该站点输入凭据。

以下是我处理方法:

$creds = Get-Credential

$basicCreds = [pscredential]::new($Creds.UserName,$Creds.Password)

Invoke-WebRequest -Uri $URL -Credential $basicCreds

结果是脚本运行器会弹出登录对话框,输入用户名和密码后,Invoke-WebRequest能够使用这些凭据访问站点。这是因为$Creds.Password已经是一个加密字符串。
我希望这可以帮助寻找类似解决方案但不想在脚本中保存用户名或密码的人。

我可以确认这种方法在macOS上的PowerShell 7.3.2中运行良好。 - Van Vangor

3
另一种方法是使用certutil.exe,将您的用户名和密码保存在一个文件中,例如in.txt,格式为username:password。
certutil -encode in.txt out.txt

现在你应该能够使用来自 out.txt 的认证值。
$headers = @{ Authorization = "Basic $((get-content out.txt)[1])" }
Invoke-WebRequest -Uri 'https://whatever' -Headers $Headers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接