Postman REST API调用成功,PowerShell Invoke-RestMethod认证失败排查
问题描述
我用Postman调用带基础认证(Basic Authentication)的API完全正常,但用Postman生成的PowerShell代码,更新了正确的基础认证信息并添加忽略无效证书的逻辑后,调用一直返回401 - Unauthorized错误。
我试过在Postman里设置代理抓包看请求详情,但没成功。现在找不到Postman和PowerShell发送请求的差异,该怎么排查?
我的PowerShell脚本
# Ignore Self-Signed or Invalid SSL certificates if (-not ([System.Management.Automation.PSTypeName]'ServerCertificateValidationCallback').Type) { $certCallback = @" using System; using System.Net; using System.Net.Security; using System.Security.Cryptography.X509Certificates; public class ServerCertificateValidationCallback { public static void Ignore() { if(ServicePointManager.ServerCertificateValidationCallback ==null) { ServicePointManager.ServerCertificateValidationCallback += delegate ( Object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors ) { return true; }; } } } "@ Add-Type $certCallback } [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12; [ServerCertificateValidationCallback]::Ignore() # example API hosts $apihosts = @( 'host1.example.com', 'host2.example.com' ) # example API endpoints $urilist = 'v2/api/list' $uridata = 'v2/api/list/{id}/entries' # get user credentials $creds = Get-Credential $creds.UserName $creds.GetNetworkCredential().Password # debugging credentials $continue = Read-Host "Did the correct password get captured? ([y]es/[n]o)" $continue.ToLower() $answer = $continue.Substring(0,1) if ($answer -eq "n") { return } else { echo "continuing..." # encode credentials in base64 $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($creds.Password) $Pass = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) $cred = "$($creds.UserName):$($Pass)" $base64creds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($cred)) $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.Add("Accept", "application/json") $headers.Add("Authorization", "BASIC $base64creds") #$headers.Add("Authorization", "Basic $base64creds") # Loop API Get #$apihost = $apihosts[0] # testing foreach ($apihost in $apihosts) { $Uri = "https://$apihost/$urilist" Write-Host "Uri: $Uri" # debugging URI # initial API call # - all three calls below fail with '401 - Unauthorized' error #$list = Invoke-RestMethod -Uri "$Uri" -Method 'GET' -Headers $headers -ErrorAction Stop -WarningAction Stop $list = Invoke-RestMethod -Uri $Uri -Method GET -Headers $headers #$list = Invoke-WebRequest -Uri $Uri -Method GET -Headers $Headers if ($list -eq $null) { exit } foreach ($item in $list.row) { $id = $item.id $name = $item.name $itemuri = "https://$apihost/" + $uridata -replace "\{id\}", $id $itemdata = Invoke-RestMethod -Uri "$itemuri" -Method 'GET' -Headers $headers -ErrorAction Stop -WarningAction Stop foreach ($entry in $itemdata.entries.entry) { # do stuff with response data <...> } # $entry } # $item } # $apihost Write-Host "DONE." }
排查方案
1. 修复认证头的大小写问题
HTTP标准中Basic认证的头值要求是Basic(首字母大写,其余小写),你脚本里写的"BASIC $base64creds"可能被严格遵循标准的服务器拒绝。把这行改成:
$headers.Add("Authorization", "Basic $base64creds")
2. 对比Postman与PowerShell的请求头
- Postman端:发送请求后点击右上角「代码」按钮(</>图标),选择「Raw」格式,复制完整请求头,和PowerShell构造的
$headers对比,检查是否遗漏或存在差异字段(比如User-Agent、Content-Type等)。 - PowerShell端:在调用
Invoke-RestMethod前添加调试代码,输出所有请求头:$headers.GetEnumerator() | ForEach-Object { Write-Host "$($_.Key): $($_.Value)" }
3. 验证凭证编码正确性
如果密码包含非ASCII字符(中文、特殊符号等),用ASCII.GetBytes编码会导致乱码,改用UTF-8编码试试:
$base64creds = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($cred))
更可靠的方式是直接使用Invoke-RestMethod的-Credential参数,自动处理认证头编码:
$list = Invoke-RestMethod -Uri $Uri -Method GET -Credential $creds -Headers @{ "Accept" = "application/json" }
4. 抓包对比完整请求
用Fiddler捕获两边的请求进行全量对比:
- PowerShell抓包:运行脚本前设置代理:
[System.Net.WebRequest]::DefaultWebProxy = New-Object System.Net.WebProxy("http://127.0.0.1:8888") [System.Net.WebRequest]::DefaultWebProxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials - Postman抓包:在「设置」-「代理」中手动设置代理为
http://127.0.0.1:8888,发送请求后即可在Fiddler中查看。
对比请求方法、URL、所有请求头的细节,找出差异点。
5. 匹配SSL/TLS协议版本
你脚本固定用Tls12,但Postman可能使用Tls13或其他协议,尝试放宽协议范围:
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 -bor [System.Net.SecurityProtocolType]::Tls13
内容的提问来源于stack exchange,提问作者skeetastax
相关产品推荐
相关产品推荐

