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

MS Graph批量拉取17k用户时出现401未授权及超时问题求助

处理MS Graph批量拉取用户时中途401未授权错误的解决方案

问题场景

测试环境运行正常,但生产环境处理17000名用户(预计耗时3-4小时)时,中途(通常处理7000-10000用户、耗时1.5-2小时左右)触发401未授权错误,导致后续JSON解析流程失败。

具体报错信息

401未授权错误

Invoke-WebRequest : The remote server returned an error: (401) Unauthorized.
At C:\temp\GatherAzureUsers.ps1:98 char:18
... ignInData = Invoke-WebRequest -Method GET -Uri $NextLink -ContentType ...
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand

JSON解析错误

ConvertFrom-JSon : Invalid JSON primitive: .
At C:\temp\GatherAzureUsers.ps1:99 char:32
$SignInData = $SignInData | ConvertFrom-JSon
                           ~~~~~~~~~~~~~~~~
CategoryInfo          : NotSpecified: (:) [ConvertFrom-Json], ArgumentException
FullyQualifiedErrorId : System.ArgumentException,Microsoft.PowerShell.Commands.ConvertFromJsonCommand

已尝试的无效操作

  • 大循环中定期执行Disconnect-MgGraph并重新连接,试过应用密钥认证和浏览器手动认证
  • 尝试设置graphServiceClient.HttpProvider.OverallTimeout = TimeSpan.FromHours(6)延长超时,PowerShell提示术语未识别,无法生效

解决方案

1. 改用Microsoft Graph PowerShell SDK自动处理令牌与分页

放弃手动调用Invoke-WebRequest处理$NextLink,使用SDK内置的自动令牌刷新和分页机制,避免手动管理令牌失效问题:

# 自动拉取所有用户,SDK会处理令牌刷新和分页
$allUsers = Get-MgUser -All $true -Select Id, DisplayName, UserPrincipalName, SignInActivity

2. 优化手动请求的令牌刷新逻辑

若必须手动调用API,每次请求前检查JWT令牌的exp字段(过期时间),剩余有效期不足5分钟时重新获取新令牌,而非定期盲目重连:

# 解析令牌获取过期时间
function Get-TokenExpiration {
    param($token)
    $payload = $token.Split('.')[1].Replace('-', '+').Replace('_', '/')
    while ($payload.Length % 4) { $payload += '=' }
    $decoded = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($payload)) | ConvertFrom-Json
    return [DateTimeOffset]::FromUnixTimeSeconds($decoded.exp).LocalDateTime
}

# 请求前检查令牌是否即将过期
$expiration = Get-TokenExpiration -token $currentToken
if ((Get-Date) -gt $expiration.AddMinutes(-5)) {
    # 重新获取令牌
    $currentToken = Get-NewAccessToken
    $headers["Authorization"] = "Bearer $currentToken"
}

3. 设置单个请求的超时参数

针对Invoke-WebRequest直接设置超时,而非尝试调用.NET SDK的属性:

# 设置单个请求超时为10分钟
$SignInData = Invoke-WebRequest -Method GET -Uri $NextLink -ContentType "application/json" -Headers $headers -TimeoutSec 600

4. 拆分任务为批次处理

将17000用户拆分为多个批次(如每批次1000人),每完成一个批次后重新初始化认证,并保存当前进度,避免长时间会话导致的令牌失效:

$batchSize = 1000
$userIds = Get-MgUser -Select Id | Select-Object -ExpandProperty Id
for ($i=0; $i -lt $userIds.Count; $i+=$batchSize) {
    $batch = $userIds[$i..($i+$batchSize-1)]
    # 重新连接Graph,确保令牌有效
    Disconnect-MgGraph -ErrorAction SilentlyContinue
    Connect-MgGraph -ClientSecretCredential $cred -TenantId $tenantId
    # 处理当前批次
    $batchData = $batch | ForEach-Object { Get-MgUser -UserId $_ -Select * }
    # 保存批次结果
    $batchData | Export-Csv -Path "UserBatch_$i.csv" -Append -NoTypeInformation
}

5. 捕获401错误并自动重试

在代码中添加错误捕获逻辑,遇到401时自动刷新令牌并重试:

$retryLimit = 3
$retryCount = 0
do {
    try {
        $SignInData = Invoke-WebRequest -Method GET -Uri $NextLink -ContentType "application/json" -Headers $headers -ErrorAction Stop
        break
    }
    catch [System.Net.WebException] {
        if ($_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode]::Unauthorized -and $retryCount -lt $retryLimit) {
            # 刷新令牌
            $currentToken = Get-NewAccessToken
            $headers["Authorization"] = "Bearer $currentToken"
            $retryCount++
            Start-Sleep -Seconds 5
        }
        else {
            throw $_
        }
    }
} while ($retryCount -lt $retryLimit)

6. 确认应用权限配置

  • 使用应用权限而非委派权限,应用权限令牌有效期最长24小时,远长于委派权限的1小时
  • 在Azure门户确认应用权限已获得管理员同意,避免中途因权限变更导致授权失败

内容的提问来源于stack exchange,提问作者MikeH

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 12:00:24