news 2026/9/6 16:56:48

Requests 认证机制完全指南:从 Basic、netrc、Digest 认证到自定义 AuthBase 认证处理器的实现原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Requests 认证机制完全指南:从 Basic、netrc、Digest 认证到自定义 AuthBase 认证处理器的实现原理

Requests 认证机制完全指南:从 Basic、netrc、Digest 认证到自定义 AuthBase 认证处理器的实现原理

【免费下载链接】requestsA simple, yet elegant, HTTP library.项目地址: https://gitcode.com/GitHub_Trending/re/requests

本文以 Requests 官方认证文档 authentication.rst 为主体,系统讲解 Requests 支持的全部认证形态:HTTP Basic 认证及其元组简写、netrc 文件认证、Digest 摘要认证、OAuth 1/OAuth 2 生态扩展,以及如何通过继承AuthBase编写自定义认证处理器。结合 src/requests/auth.py、src/requests/sessions.py 与 src/requests/models.py 的源码,你可以不仅会"传一个 auth 参数",还能理解认证头究竟在请求生命周期的哪一步被写入、netrc 凭据按什么规则查找、Digest 认证的 401 质询-重试协议如何在底层实现,以及重定向时凭据如何被安全地剥离与重建。

认证在 Requests 中的统一抽象:AuthBase

Requests 中所有认证方式都收敛到同一个抽象:认证对象必须是"可调用对象",接收一个已准备好的PreparedRequest,修改其请求头后返回。这一点由基类定义得很直白:

# src/requests/auth.py class AuthBase: """Base class that all auth implementations derive from""" def __call__(self, r: PreparedRequest) -> PreparedRequest: raise NotImplementedError("Auth hooks must be callable.")

auth参数在准备阶段的处理逻辑位于 PreparedRequest.prepare_auth,它决定了三类输入分别如何被归一化:

# src/requests/models.py(prepare_auth 核心片段) def prepare_auth(self, auth, url=""): # 1. 未显式提供 auth 时,先尝试从 URL 中提取凭据(如 http://user:pass@host/) if auth is None: url_auth = get_auth_from_url(cast(str, self.url)) auth = url_auth if any(url_auth) else None if auth: # 2. 二元元组被特殊处理为 Basic Auth if isinstance(auth, tuple) and len(auth) == 2: auth_handler = HTTPBasicAuth(*auth) else: # 3. 其他情况一律视为可调用的认证处理器 auth_handler = cast("Callable[..., PreparedRequest]", auth) # 允许认证对象对请求做修改 r = auth_handler(self) # 用修改后的请求状态更新自身 self.__dict__.update(r.__dict__) # 重新计算 Content-Length self.prepare_content_length(self.body)

这解释了后文所有认证形式的通用行为:认证处理器在请求"准备"阶段被调用,而不是发送阶段。处理器对PreparedRequest做的一切修改(写入Authorization头、注册 hooks)都会被合并回请求本身。

HTTP Basic 认证:最直接的认证方式

许多 Web 服务接受 HTTP Basic 认证,这是最简单的形态,Requests 开箱即用:

>>> from requests.auth import HTTPBasicAuth >>> basic = HTTPBasicAuth('user', 'pass') >>> requests.get('https://your-host.example/basic-auth/user/pass', auth=basic) <Response [200]>

由于 Basic 认证足够常见,Requests 提供了一个简写——直接传(username, password)二元组:

>>> requests.get('https://your-host.example/basic-auth/user/pass', auth=('user', 'pass')) <Response [200]>

传入元组与上面使用HTTPBasicAuth实例的效果完全一致,因为prepare_auth会将其转换为HTTPBasicAuth(*auth)

源码细节:Authorization 头是如何构造的

HTTPBasicAuth.__call__的实现只有两行(见 HTTPBasicAuth):

def __call__(self, r: PreparedRequest) -> PreparedRequest: r.headers["Authorization"] = _basic_auth_str(self.username, self.password) return r

真正的工作在 _basic_auth_str 中完成,有几个值得注意的实现细节:

  • 编码规则:若用户名/密码是str,会先按latin1编码为bytes,再拼接username:password做 Base64 编码,最后加上Basic前缀。这与 RFC 中 Basic 凭证的构造方式一致。
  • 类型兼容警告:为了向后兼容历史上传入非字符串(如整数)的用法,源码保留了一段兼容代码:非str/bytes的凭据会被转为字符串并触发DeprecationWarning,源码注释明确说明该行为将在 3.0.0 移除。
  • 同族类 HTTPProxyAuth:HTTPProxyAuth 继承自HTTPBasicAuth,仅把目标头从Authorization换成Proxy-Authorization,用于代理认证场景。

netrc 认证:从文件按主机名自动取凭据

auth参数未显式给出时,Requests 会尝试从用户的 netrc 文件中查找目标 URL 主机名的凭据。这一默认行为由 Session.prepare_request 触发:

# src/requests/sessions.py(prepare_request 片段) # 若未显式设置 basic authentication,则尝试从环境(netrc)中读取 auth = request.auth if self.trust_env and not auth and not self.auth: auth = get_netrc_auth(url)

按官方文档的约定:

  • netrc 文件优先级高于headers=手工设置的原始 HTTP 认证头;
  • 若在某主机名下找到凭据,请求将以HTTP Basic 认证方式发出;
  • Requests 按顺序在~/.netrc~/_netrc、以及环境变量NETRC指定的路径中查找(~在 Unix 下是$HOME,Windows 下是%USERPROFILE%)。

源码细节:get_netrc_auth 的查找与容错逻辑

查找逻辑在 get_netrc_auth 中,行为要点:

netrc_file = os.environ.get("NETRC") if netrc_file is not None: netrc_locations = (netrc_file,) else: netrc_locations = (f"~/{f}" for f in NETRC_FILES) # ".netrc", "_netrc"
  • 环境变量NETRC存在时只查这一个路径,不再回退到~/.netrc
  • 对 URL 做urlparse取出hostname,用标准库netrc.netrc(path).authenticators(host)按主机名匹配;
  • 对"login 为空"的 netrc 条目(仅password字段)做了兼容:login_i = 0 if _netrc[0] else 1,即自动改用第二个字段作为登录名;
  • netrc 文件解析失败(NetrcParseError)或读取权限问题(OSError)时静默跳过netrc 认证,不抛出异常——除非显式传入raise_errors=True

用 trust_env 关闭 netrc 行为

netrc 的读取受 Session 的trust_env开关控制(默认True,见 Session 初始化)。在需要隔离环境凭据的场景(如测试、多租户服务)可以显式关闭:

>>> s = requests.Session() >>> s.trust_env = False >>> s.get('https://your-host.example/basic-auth/user/pass')

设置为False后,prepare_requestif self.trust_env and ...的 netrc 分支不再执行,同时重定向时的 netrc 重建(见下文)也会被跳过。

Digest 认证:两次往返的质询-应答协议

Digest 认证是另一种非常流行的 HTTP 认证形式,Requests 同样开箱即用:

>>> from requests.auth import HTTPDigestAuth >>> url = 'https://your-host.example/digest-auth/auth/user/pass' >>> requests.get(url, auth=HTTPDigestAuth('user', 'pass')) <Response [200]>

表面用法和 Basic 一样简单,但底层实现复杂得多——Digest 协议要求先用一次"裸"请求换取服务器质询(nonce),再携带计算出的摘要重发。这部分逻辑全部封装在 HTTPDigestAuth 中:

认证对象注册 response hooks

HTTPDigestAuth.__call__并不直接写Authorization头,而是做了三件事(见call):

def __call__(self, r: PreparedRequest) -> PreparedRequest: self.init_per_thread_state() # 若已有缓存的 nonce,直接构造摘要头,跳过 401 质询 if self._thread_local.last_nonce: _digest_auth = self.build_digest_header(r.method, r.url) if _digest_auth: r.headers["Authorization"] = _digest_auth # 记录 body 的文件位置,便于 401 后重新发送 if (tell := getattr(r.body, "tell", None)) is not None: self._thread_local.pos = tell() # 注册两个响应 hooks:处理 401 与重定向 r.register_hook("response", self.handle_401) r.register_hook("response", self.handle_redirect) self._thread_local.num_401_calls = 1 return r

这里正是官方文档所说"部分认证形式会额外注册 hooks 以提供进一步功能"的实例:认证处理器在请求准备阶段挂上responsehooks,由 hooks 系统 在拿到响应后按序分发。

401 处理:解析质询、重放请求

handle_401(见 handle_401)是协议的核心:

  1. 只对4xx响应尝试认证(非 4xx 直接放行,源码注释引用了该行为的 issue 记录);
  2. 检查WWW-Authenticate头中是否含digest,且 401 重试次数num_401_calls < 2(即最多重试一次);
  3. 若请求体是文件对象,先seek回记录的位置,保证重放时 body 完整;
  4. parse_dict_header解析出 realm/nonce/qop/algorithm 等质询参数,复制到请求副本上附加 Digest 头后,复用原连接重新发送,并把第一次的 401 响应挂进history
  5. 若重试后仍失败,则复位num_401_calls并返回原始响应,交由调用方处理。

摘要计算:build_digest_header

摘要头的构造在 build_digest_header,实现了对 RFC 2069/2617 主要算法的覆盖:

服务端声明的 algorithm实际哈希备注
未声明MD5协议默认值
MD5/MD5-SESSMD5MD5-SESS额外把 nonce 与 cnonce 折叠进 HA1
SHASHA-1源码中按算法名SHA匹配
SHA-256SHA-256
SHA-512SHA-512

关键计算步骤:A1 = username:realm:passwordA2 = METHOD:path(path 取 request-uri,为空时回退为/,query 会拼回),HA1/HA2分别取哈希后按qop计算respdig。此外实现还处理了两个容易踩坑的状态问题:

  • 线程局部状态last_noncenonce_countchal等全部存放在threading.local()中(init_per_thread_state),同一认证实例可被多线程安全复用;
  • nonce 计数:当服务端复用上次的 nonce 时nonce_count自增并格式化为 8 位十六进制(nc),cnonce则由 nonce、计数、当前时间与 8 字节随机数做 SHA-1 后截断 16 位生成;
  • 重定向复位handle_redirecthook 在遇到重定向时把num_401_calls复位为 1,允许在跳转后的新 URL 上重新走一轮质询。

OAuth 认证:借助 requests-oauthlib 生态扩展

OAuth 是很多 Web API 的主流认证形式,但不在 Requests 核心实现内,官方文档指引使用社区库requests-oauthlib

OAuth 1

>>> import requests >>> from requests_oauthlib import OAuth1 >>> url = 'https://api.twitter.com/1.1/account/verify_credentials.json' >>> auth = OAuth1('YOUR_APP_KEY', 'YOUR_APP_SECRET', ... 'USER_OAUTH_TOKEN', 'USER_OAUTH_TOKEN_SECRET') >>> requests.get(url, auth=auth) <Response [200]>

OAuth 流程的细节请以 OAuth 官方规范与requests-oauthlib项目仓库为准(仓库内不维护该库的实现)。

OAuth 2 与 OpenID Connect

requests-oauthlib同样覆盖 OAuth 2,它是 OpenID Connect 的底层认证机制。该库提供四种凭据管理流程,可按应用形态选择:

  • Web Application Flow:有服务端、可安全保管 client secret 的网页应用;
  • Mobile Application Flow:无服务端、secret 无法保密的移动/桌面应用;
  • Legacy Application Flow:旧式(password 模式)流程;
  • Backend Application Flow:服务端到服务端(client_credentials)场景。

由于 Requests 的认证接口是"可调用对象"这一开放约定,requests-oauthlib的 auth 对象可以无缝传入auth=参数,与本文其他认证方式使用方式一致。

其他认证形态:Kerberos 与 NTLM

Requests 的设计允许其他认证形式被便捷地插入,开源社区为更复杂或较少使用的认证编写了处理器的典型代表是:

  • Kerberosrequests-kerberos):常用于企业内网、KDC 体系下的服务互信;
  • NTLMrequests-ntlm):常见于 Windows 域环境。

这两个项目均维护在 Requests 官方组织名下,使用前按其各自仓库的说明安装配置即可,核心库中不包含它们的实现。

自定义认证:继承 AuthBase 编写新处理器

当现有实现不能满足需求时,可以自己实现一种认证形式。方法就是继承requests.auth.AuthBase并实现__call__()方法:

>>> import requests >>> class MyAuth(requests.auth.AuthBase): ... def __call__(self, r): ... # 在此实现我的认证逻辑,例如写入自定义头 ... r.headers["X-Api-Key"] = "my-secret-key" ... return r ... >>> url = 'https://your-host.example/get' >>> requests.get(url, auth=MyAuth()) <Response [200]>

结合前面 prepare_auth 的源码可以给出两条编写要点:

  1. __call__在请求准备阶段执行,因此它必须完成让认证生效所需的全部工作(修改请求头是最常见的做法)。由于prepare_auth在调用后会用返回对象的__dict__更新自身并重新计算Content-Length,处理器修改了 body 也能被正确反映到最终请求中;
  2. 复杂协议可以注册 hooks。Digest 认证就是范例:__call__里只负责"能省则省"(有缓存 nonce 就直接带摘要头),并把 401 重试逻辑挂在responsehook 上。如果你的认证协议也需要"看响应再决定"(如多因子挑战、令牌刷新),同样的模式可以直接套用。

更多实现范例可参考 auth.py 中的HTTPBasicAuthHTTPProxyAuthHTTPDigestAuth三个内置类,以及 Requests 组织名下的社区认证库。

重定向时的凭据安全:rebuild_auth

一个容易被忽略但生产上重要的细节:跟随重定向时,Authorization 头可能把凭据泄漏到另一个域。Requests 在 Session.rebuild_auth 中做了"智能剥离与重建":

# src/requests/sessions.py(rebuild_auth 核心片段) if "Authorization" in headers and self.should_strip_auth(original_url, url): # 重定向到新主机时,剥离认证头,避免凭据泄漏 del headers["Authorization"] # netrc 中可能为新主机准备了凭据,重新应用 new_auth = get_netrc_auth(url) if self.trust_env else None if new_auth is not None: prepared_request.prepare_auth(new_auth)

即:跨主机跳转时删除Authorization头,同时(在trust_envTrue时)为新主机重新查一次 netrc。这与 Digest 的handle_redirect复位逻辑共同保证了多跳认证场景下凭据既不会泄漏、也不会丢失。

小结

Requests 的认证体系可以归纳为一张分层地图:

  • 协议内置:Basic(HTTPBasicAuth/元组简写/URL 内嵌凭据)、Proxy(HTTPProxyAuth)、Digest(HTTPDigestAuth,含 401 质询-重试、nonce 计数、多算法支持)——全部位于 src/requests/auth.py;
  • 环境集成:netrc 按主机名自动取凭据,受trust_env控制,解析逻辑在 src/requests/utils.py;
  • 生态扩展:OAuth 1/OAuth 2/OpenID Connect 由requests-oauthlib提供,Kerberos/NTLM 由 Requests 组织维护的社区库提供;
  • 开放扩展点AuthBase+auth=可调用约定 + response hooks,使任何新协议都能以"写一个类"的成本接入。

理解prepare_auth(认证在准备阶段介入)与responsehooks(认证可在响应后介入)这两个时机点,就掌握了 Requests 认证机制的全部骨架,无论是排查认证失效问题还是开发自定义处理器,都可以从这两处入手。

【免费下载链接】requestsA simple, yet elegant, HTTP library.项目地址: https://gitcode.com/GitHub_Trending/re/requests

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/6 16:56:05

过程设备设计期末复习:四大失效模式与核心计算考点全梳理

简介&#xff1a;过程设备设计是化工、能源等领域的重要课程&#xff0c;期末复习往往涉及压力容器规范、材料特性与强度分析等多个模块。这份复习资料面向正在备考《过程设备设计》的学生&#xff0c;围绕课程高频考点进行系统梳理&#xff0c;覆盖ASME规范、薄壁与厚壁容器应…

作者头像 李华
网站建设 2026/9/6 16:54:26

Ansys随机振动分析全解析:从PSD谱输入到3σ应力评估

简介&#xff1a;《Ansys培训随机振动分析.ppt》是一份面向Ansys Workbench初、中级用户的随机振动&#xff08;PSD&#xff09;分析培训文档&#xff0c;适用于航空、航天、机械、土木等领域中需要评估结构在随机激励下动态响应的工程师。文档基于概率谱分析视角&#xff0c;系…

作者头像 李华
网站建设 2026/9/6 16:53:10

二十四寸圆盘拉伸机直流调速系统设计:双闭环与参数整定全解析

简介&#xff1a;面向自动化与控制专业学生的完整课程设计报告&#xff0c;围绕二十四寸圆盘拉伸机直流调速系统展开&#xff0c;从设计目的、调速方案选型到主回路参数计算均有详细说明&#xff0c;适合运动控制系统课程设计或相关毕设参考。压缩包内仅有1个Word文档&#xff…

作者头像 李华
网站建设 2026/9/6 16:52:53

嵌入式Linux系统移植实战:从U-Boot到根文件系统全流程解析

简介&#xff1a;嵌入式Linux系统移植是构建嵌入式应用平台的核心前提。这份PDF技术文献系统介绍了将Linux操作系统移植到ARM开发板的完整流程&#xff0c;内容涵盖交叉编译工具链的安装、内核编译与配置、设备驱动程序移植、根文件系统制作与优化&#xff0c;以及系统测试调整…

作者头像 李华
网站建设 2026/9/6 16:50:40

量子蒙特卡洛期权定价与误差控制:系统方案与技术拆解

简介&#xff1a;这份403页的《量子金融交易系统设计方案详解》专题文档&#xff0c;聚焦量子计算与金融工程交叉领域&#xff0c;系统阐述基于量子蒙特卡洛模拟的期权定价方法、误差动态控制机制及计算效率优化路径&#xff0c;适合正在研究量子金融算法、金融衍生品定价模型或…

作者头像 李华
网站建设 2026/9/6 16:49:25

量子蒙特卡洛如何重塑期权定价?从量子振幅估计到IQAE工程实践

简介&#xff1a;一份共403页的量子金融交易系统设计方案PDF文档&#xff0c;围绕期权定价量子蒙特卡洛模拟的误差动态控制与计算效率优化展开&#xff0c;面向金融工程、量化交易与量子计算交叉领域的研发人员及研究人员。文档共51个大章节&#xff0c;系统梳理了量子金融系统…

作者头像 李华