首页 > 解决方案 > urllib.request 在行为上与 curl 或 httpx 有何不同?在向 Google Container Registry 的请求中获取 401

问题描述

我目前正在编写一些代码来与 Google Container Registry 上的图像进行交互。我有使用plaincurl和also的工作代码httpx。我正在尝试构建一个没有 3rd 方依赖项的包。我的好奇心是围绕一个特定的端点,我从该端点获得了 curl 和 httpx 的成功响应,但是 401 Unauthorized using urllib.request.

演示我要实现的目标的 bash 脚本如下。它从注册表 API 检索访问令牌,然后使用该令牌来验证 API 确实运行版本 2 并尝试访问特定的 Docker 映像配置。恐怕为了对此进行测试,您将需要访问私有 GCR 图像和其中一个标签的摘要。

#!/usr/bin/env bash

set -eu

token=$(gcloud auth print-access-token)
image=...
digest=sha256:...

get_token() {
    curl -sSL \
        -G \
        --http1.1 \
        -H "Authorization: Bearer ${token}" \
        -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
        --data-urlencode "scope=repository:$1:pull" \
        --data-urlencode "service=gcr.io" \
        "https://gcr.io/v2/token" | jq -r '.token'
}

echo "---"
echo "Retrieving access token."
access_token=$(get_token ${image})

echo
echo "---"
echo "Testing version 2 capability with access token."
curl -sSL \
    --http1.1 \
    -o /dev/null \
    -w "%{http_code}" \
    -H "Authorization: Bearer ${access_token}" \
    -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
    https://gcr.io/v2/

echo
echo "---"
echo "Retrieving image configuration with access token."
curl -vL \
    --http1.1 \
    -o /dev/null \
    -w "%{http_code}" \
    -H "Authorization: Bearer ${access_token}" \
    -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
    "https://gcr.io/v2/${image}/blobs/${digest}"

我还创建了两个 Jupyter 笔记本,在httpx和 bare中展示了我的解决方案urllib.request。httpx 完美运行,而 urllib 在图像配置请求上以某种方式失败。试图找出差异的想法已经不多了。如果您自己运行笔记本,您将看到被调用的 URL 包含一个令牌作为查询参数(这是安全问题吗?)。当我打开该链接时,我实际上可以自己成功下载数据。也许 urllib 仍然使用承载令牌传递授权标头,使最后一次调用失败并出现 401 未授权?

非常感谢任何见解。

标签: python-3.xgoogle-cloud-platformurllibgoogle-container-registryhttpx

解决方案


我做了一些调查,我相信不同之处在于最后一次调用"https://gcr.io/v2/${image}/blobs/${digest}"实际上包含重定向。检查curlandhttpx调用显示,两者都不包含Authorization第二个重定向请求中的标头,而在我在笔记本中设置的方式中,urllib.request始终包含此标头。这导致 401 有点奇怪,但现在我知道如何解决它。

编辑:我现在可以确认,通过构建一个urllib.request.Request实例,与链接的笔记本不同,使用请求的add_unredirected_header方法添加授权标头,一切都按预期工作。


推荐阅读