You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.09 21:14:48