Authlib 为 HTTPX2 提供 OAuth 1.0 支持:OAuth1Client 与 AsyncOAuth1Client 完整实战指南
【免费下载链接】authlibThe ultimate Python library in building OAuth, OpenID Connect clients and servers. JWS, JWE, JWK, JWA, JWT included.项目地址https://gitcode.com/gh_mirrors/au/authlib点击查看免费下载Authlib 在authlib.integrations.httpx_client模块中为新一代 Python HTTP 客户端 HTTPX2 提供了完整的 OAuth 1.0RFC 5849能力核心是同步的OAuth1Client与异步的AsyncOAuth1Client两个客户端类。本文以官方文档为主体结合仓库源码与测试用例系统讲解如何在 HTTPX2 中完成 OAuth 1.0 三步授权流程、构造签名、发起受保护资源请求并剖析其底层实现原理帮助你在同步与异步两种编程模型下都能正确接入支持 OAuth 1.0 的服务商如 Twitter。HTTPX2 与 OAuth 1.0 客户端概览HTTPX2 是 Python 生态中的新一代 HTTP 客户端支持同步与异步两种调用方式。Authlib 在 httpx_client 模块 中为其注入了 OAuth 1.0 能力对外暴露两个核心类OAuth1Client同步客户端直接继承httpx2.ClientAsyncOAuth1Client异步客户端直接继承httpx2.AsyncClient。这两个类与requests_client中的OAuth1Session共享同一套 API 设计都是基于 authlib/oauth1/client.py 中的通用OAuth1Client基类实现的。如果你已经熟悉 requests 版本的 OAuth 1.0 客户端切换到 HTTPX2 几乎不需要学习成本通用的 OAuth 1.0 Session 流程指南见 OAuth 1 Session 通用指南同目录下还提供了 API 参考 与 Requests 集成文档。OAuth 1.0 获取访问令牌的流程固定为三步获取临时凭证temporary credential——向服务商请求一个 request token访问授权页面——让资源所有者用户在浏览器中授权用临时凭证交换访问令牌——换取 access token。初始化 OAuth 1.0 客户端无论同步还是异步初始化都只需要client_id与client_secret两个必填参数。从源码oauth1_client.py可以看出两个类的构造签名完全一致client_id, client_secretNone, tokenNone, token_secretNone, redirect_uriNone, rsa_keyNone, verifierNone, signature_methodSIGNATURE_HMAC_SHA1, signature_typeSIGNATURE_TYPE_HEADER, force_include_bodyFalse, **kwargs,对应的初始化示例# 同步客户端 from authlib.integrations.httpx_client import OAuth1Client client OAuth1Client(client_id, client_secret) # 异步客户端 from authlib.integrations.httpx_client import AsyncOAuth1Client client AsyncOAuth1Client(client_id, client_secret)两个构造器内部都会先调用extract_client_kwargs从kwargs中筛选出属于 HTTPX2 客户端的专有参数见 utils.py再分别初始化httpx2.Client/httpx2.AsyncClient和 OAuth 1 客户端逻辑。也就是说HTTPX2 自身的全部客户端能力超时、代理、证书校验、事件钩子等依然可用其中被提取的HTTPX_CLIENT_KWARGS白名单包括headers, cookies, verify, cert, http1, http2, proxy, mounts, timeout, follow_redirects, limits, max_redirects, event_hooks, base_url, transport, trust_env, default_encoding关键构造参数说明参数默认值说明client_id必填客户端标识consumer key缺失时抛出ValueError(Missing client_id)client_secretNone客户端密钥consumer secret参与签名计算token/token_secretNone已获得的 token 与对应密钥用于访问受保护资源或恢复会话redirect_uriNone授权回调地址可事后通过client.redirect_uri属性修改rsa_keyNone使用 RSA-SHA1 签名时的私钥verifierNone授权回调中携带的 verifiersignature_methodSIGNATURE_HMAC_SHA1签名算法可选HMAC-SHA1、RSA-SHA1、PLAINTEXTsignature_typeSIGNATURE_TYPE_HEADER签名参数放置位置可选HEADER、BODY、QUERYforce_include_bodyFalse强制把请求体纳入签名对非 form-encoded 请求体也生效这些签名常量统一来自 signature.pySIGNATURE_HMAC_SHA1 HMAC-SHA1、SIGNATURE_RSA_SHA1 RSA-SHA1、SIGNATURE_PLAINTEXT PLAINTEXT以及SIGNATURE_TYPE_HEADER/QUERY/BODY。三步授权流程实战下面以同步客户端为例走通完整流程异步客户端 API 完全一致只需按需添加await。第一步获取临时凭证request_token_url https://api.twitter.com/oauth/request_token request_token client.fetch_request_token(request_token_url) print(request_token) # {oauth_token: gA..H, oauth_token_secret: lp..X, oauth_callback_confirmed: true}fetch_request_token会向请求令牌端点发起一个已签名的 POST 请求并从application/x-www-form-urlencoded响应中解析出 token 字典。如果希望回调地址与注册时不同可以在获取之前修改client.redirect_uri https://your-domain.org/auth client.fetch_request_token(request_token_url)拿到后应保存这份临时凭证dict供授权回调后恢复会话使用。第二步生成并访问授权 URLauthenticate_url https://api.twitter.com/oauth/authenticate client.create_authorization_url(authenticate_url, request_token[oauth_token]) # https://api.twitter.com/oauth/authenticate?oauth_tokengA..H由于 session 已复用第二个参数request_token其实可以省略客户端会自动从内部状态取用client.create_authorization_url(authenticate_url)源码中client.py 的create_authorization_url会把oauth_token追加到 URL并在设置了redirect_uri时同时追加oauth_callback参数。将生成的 URL 交给用户访问并完成授权即可。第三步交换访问令牌授权完成后服务商会把用户重定向回你注册或指定的回调地址例如https://example.com/twitter?oauth_tokengA..Hoauth_verifierfcg..1Dq使用回调 URL 解析出授权响应然后换取访问令牌resp_url https://example.com/twitter?oauth_tokengA..Hoauth_verifierfcg..1Dq client.parse_authorization_response(resp_url) access_token_url https://api.twitter.com/oauth/access_token token client.fetch_access_token(access_token_url) print(token) # {oauth_token: 12345-st..E, oauth_token_secret: o67..X, # user_id: 12345, screen_name: lepture, x_auth_expires: 0} save_access_token(token)注意fetch_access_token要求必须持有 verifier如果 verifier 缺失会抛出OAuthError(missing_verifier, ...)如果令牌端点返回 4xx/5xx则抛出OAuthError(fetch_token_denied, ...)见 client.py 的parse_response_token。获取成功后 verifier 会被自动清空。会话中断后的恢复场景真实项目中用户跳转到授权页后你的 session 通常已经结束。当服务商回调到你的服务器时需要基于保存的临时凭证重建客户端# 从存储中恢复 request token request_token restore_request_token() oauth_token request_token[oauth_token] oauth_token_secret request_token[oauth_token_secret] # 异步场景下改用 AsyncOAuth1Client client OAuth1Client( client_id, client_secret, tokenoauth_token, token_secretoauth_token_secret, ) # 若能直接拿到 verifier则无需 parse_authorization_response verifier request.args.get(verifier) token client.fetch_access_token(access_token_url, verifier)同理后续访问受保护资源时也需要从数据库恢复访问令牌并重建客户端access_token restore_access_token_from_database() client OAuth1Client( client_id, client_secret, tokenaccess_token[oauth_token], token_secretaccess_token[oauth_token_secret], ) resp client.get(account_url)异步 OAuth 1.0AsyncOAuth1Client 与 awaitAsyncOAuth1Client与同步版本的行为完全一致唯一区别是所有网络操作都需要加await。文档给出的核心用法# 获取 request token request_token await client.fetch_request_token(request_token_url) # 获取 access token access_token await client.fetch_access_token(access_token_url) # 普通请求 await client.get(...) await client.post(...) await client.put(...) await client.delete(...)从源码看异步客户端在fetch_access_token的 verifier 处理上与同步版保持一致传入verifier时先写入self.auth.verifier缺失时抛OAuthError换取成功后清空。其_fetch_token内部通过await self.post(url, **kwargs)发起请求并await resp.aread()读取响应体见 oauth1_client.py。由于AsyncOAuth1Client本身继承自httpx2.AsyncClient它可以像原生 async client 一样使用async with上下文管理器from authlib.integrations.httpx_client import AsyncOAuth1Client async with AsyncOAuth1Client(id, secret, transporttransport) as client: response await client.fetch_request_token(oauth_url)这一模式在仓库测试 test_async_oauth1_client.py 中有完整体现。底层实现原理OAuth1Auth 如何给请求签名HTTPX2 的 OAuth 1 签名由OAuth1Auth类完成它同时继承自httpx2.Auth与ClientAuth见 oauth1_client.pyclass OAuth1Auth(Auth, ClientAuth): requires_request_body True def auth_flow(self, request): url, headers, body self.prepare( request.method, str(request.url), request.headers, request.content ) headers[Content-Length] str(len(body)) yield build_request(urlurl, headersheaders, bodybody, initial_requestrequest)requires_request_body True告诉 HTTPX2 在签名前必须先读取请求体auth_flow则调用ClientAuth.prepare完成签名渲染并通过build_request见 utils.py重建请求同时保留原始请求的 extensions确保重定向、超时等上下文信息不丢失。签名过程的核心逻辑在 client_auth.pyprepare根据signature_type决定参数注入位置HEADER写入Authorization: OAuth ...头BODY要求 Content-Type 为application/x-www-form-urlencoded把参数拼进表单体QUERY则把参数追加到 URI 查询串。sign生成oauth_nonce、oauth_timestamp并基于get_oauth_params组装 OAuth 参数oauth_version1.0、oauth_signature_method、oauth_consumer_key等有 token、callback、verifier 时也会带上。对非 form-encoded 且非空请求体还会按照draft-eaton-oauth-bodyhash-00计算并追加oauth_body_hash。最终通过get_oauth_signature调起SIGNATURE_METHODS注册表中对应的签名函数追加oauth_signature后再渲染一次最终请求。三种签名算法ClientAuth.SIGNATURE_METHODS注册了三种算法对应 signature.py 中的实现HMAC-SHA1默认sign_hmac_sha1先按 RFC 5849 Section 3.4 构造签名基串METHODnormalized_urinormalized_params再用client_secret token_secret作为 HMAC-SHA1 密钥输出 base64 签名RSA-SHA1sign_rsa_sha1使用rsa_key对签名基串做 RSASSA-PKCS1-v1_5 签名实现见 rsa.pyPLAINTEXTsign_plaintext不依赖签名基串直接拼接escape(client_secret) escape(token_secret)按规范必须配合 TLS/SSL 传输层使用。此外ClientAuth.register_signature_method允许注册自定义签名方法扩展点设计清晰。使用 OAuth1Auth 单独为请求签名如果你已经持有访问令牌也可以不创建完整客户端而是直接构造OAuth1Auth实例并作为 HTTPX2 的 auth 参数使用from authlib.integrations.httpx_client import OAuth1Auth auth OAuth1Auth( client_idYOUR-CLIENT-ID, client_secretYOUR-CLIENT-SECRET, tokenoauth_token value, token_secretoauth_token_secret value, ) url https://api.twitter.com/1.1/account/verify_credentials.json resp await httpx2.get(url, authauth)同步场景下把await去掉即可。这一用法与 requests_client 的 OAuth1Auth 保持对称。三种签名放置方式的验证与选择仓库测试用例对签名类型进行了严格验证。以 test_oauth1_client.py 为例HEADER默认请求的authorization头包含oauth_consumer_keyid、oauth_signature...且参数无重复键BODYauthorization头为空oauth_consumer_keyidoauth_signature...出现在表单体中QUERYauthorization头为空oauth_consumer_keyidoauth_signature...出现在 URL 查询串中。对应的异步测试在 test_async_oauth1_client.py 中两者还会通过verify_hmac_sha1对服务端收到的签名做反向校验。选择建议默认HEADER兼容性最好多数 OAuth 1.0 服务商都支持BODY适合服务商只解析表单参数的场景QUERY会把签名暴露在 URL 中容易进入日志与历史记录一般仅在服务商强制要求时使用。兼容性说明httpx2 与旧版 httpxhttpx_client/_compat.py 提供了兼容垫片优先导入httpx2若不可用则回退到httpx并触发deprecate警告。也就是说当前仓库以 HTTPX2 为标准目标环境旧版httpx仅作为过渡期的回退选项未来版本会移除该回退。开发环境中请确保安装满足httpx22.0见 pyproject.toml 的clients依赖组。总结Authlib 通过OAuth1Client与AsyncOAuth1Client为 HTTPX2 提供了与 requests 版完全对齐的 OAuth 1.0 客户端体验三步授权流程、可复用的会话状态、三种签名算法与三种参数放置方式一应俱全异步版仅在需要网络 I/O 处增加await。其底层签名与解析逻辑复用authlib.oauth1.rfc5849的通用实现并被仓库中的同步、异步两套测试完整覆盖可直接信任并用于生产接入。赞分享【免费下载链接】authlibThe ultimate Python library in building OAuth, OpenID Connect clients and servers. JWS, JWE, JWK, JWA, JWT included.项目地址https://gitcode.com/gh_mirrors/au/authlib点击查看免费下载相关推荐Elixir 状态管理实战用 Agent 封装可共享状态从 KV.Bucket 入门 OTPElixir 状态管理实战用 Agent 封装可共享状态从 KV.Bucket 入门 OTP 本文是 Elixir 官方 Mix OTP 指南 的“用Authlib OAuth 1.0服务器搭建RFC5849协议完整实现Authlib OAuth 1.0服务器搭建RFC5849协议完整实现 Authlib是Python生态中构建OAuth和OpenID Connect客户端与Vuex TypeScript 支持实战为 $store 属性与 useStore 函数提供完整类型Vuex TypeScript 支持实战为 $store 属性与 useStore 函数提供完整类型 本文围绕 Vuex 官方文档中的 TypeScript前端上一篇ng-table分页API详解编程式翻页、切换每页条数与自定义pager模板下一篇forkd命令行速查手册snapshot、fork、eval、pack等10个核心命令入门教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考