httpx 自定义身份验证
发出请求或实例化Client
时,auth
参数可用于传递要使用的身份验证方案。auth
参数可能是以下之一...
-
username
/password
的双元组,用于基本身份验证。 -
httpx.BasicAuth()
或httpx.DigestAuth()
的实例。 - 可调用的,接受请求并返回经过身份验证的请求实例。
-
httpx.Auth
的子类的实例。
其中涉及最多的是最后一个,它允许您创建涉及一个或多个请求的身份验证流。httpx.Auth
的子类应该实现 def auth_flow(request)
,并生成需要发出的任何请求...
class MyCustomAuth(httpx.Auth):
def __init__(self, token):
self.token = token
def auth_flow(self, request):
# Send the request, with a custom `X-Authentication` header.
request.headers['X-Authentication'] = self.token
yield request
如果身份验证流需要多个请求,您可以发出多个yield
,并在每种情况下获取响应...
class MyCustomAuth(httpx.Auth):
def __init__(self, token):
self.token = token
def auth_flow(self, request):
response = yield request
if response.status_code == 401:
# If the server issues a 401 response then resend the request,
# with a custom `X-Authentication` header.
request.headers['X-Authentication'] = self.token
yield request
自定义身份验证类设计为不执行任何 I/O,以便它们可以同时用于同步和异步Client
实例。如果要实现需要请求正文的身份验证方案,则需要使用requires_request_body
属性在类上指示这一点。
然后,您将能够在.auth_flow()
方法中访问request.content
。
class MyCustomAuth(httpx.Auth):
requires_request_body = True
def __init__(self, token):
self.token = token
def auth_flow(self, request):
response = yield request
if response.status_code == 401:
# If the server issues a 401 response then resend the request,
# with a custom `X-Authentication` header.
request.headers['X-Authentication'] = self.sign_request(...)
yield request
def sign_request(self, request):
# Create a request signature, based on `request.method`, `request.url`,
# `request.headers`, and `request.content`.
...
同样,如果要实现需要访问响应正文的方案,请使用requires_response_body
属性。然后,您将能够访问响应正文属性和方法,例如response.content
、response.text
、response.json()
等。
class MyCustomAuth(httpx.Auth):
requires_response_body = True
def __init__(self, access_token, refresh_token, refresh_url):
self.access_token = access_token
self.refresh_token = refresh_token
self.refresh_url = refresh_url
def auth_flow(self, request):
request.headers["X-Authentication"] = self.access_token
response = yield request
if response.status_code == 401:
# If the server issues a 401 response, then issue a request to
# refresh tokens, and resend the request.
refresh_response = yield self.build_refresh_request()
self.update_tokens(refresh_response)
request.headers["X-Authentication"] = self.access_token
yield request
def build_refresh_request(self):
# Return an `httpx.Request` for refreshing tokens.
...
def update_tokens(self, response):
# Update the `.access_token` and `.refresh_token` tokens
# based on a refresh response.
data = response.json()
...
如果确实需要执行 HTTP 请求以外的 I/O(如访问基于磁盘的缓存),或者需要使用并发基元(如锁),则应覆盖 sync_auth_flow()
.和 async_auth_flow()
(而不是auth_flow()
)。前者将由httpx.Client
使用,而后者将由httpx.AsyncClient
使用。
import asyncio
import threading
import httpx
class MyCustomAuth(httpx.Auth):
def __init__(self):
self._sync_lock = threading.RLock()
self._async_lock = asyncio.Lock()
def sync_get_token(self):
with self._sync_lock:
...
def sync_auth_flow(self, request):
token = self.sync_get_token()
request.headers["Authorization"] = f"Token {token}"
yield request
async def async_get_token(self):
async with self._async_lock:
...
async def async_auth_flow(self, request):
token = await self.async_get_token()
request.headers["Authorization"] = f"Token {token}"
yield request
如果您只想支持这两种方法之一,则仍应重写它,但应提出显式 RuntimeError
。
import httpx
import sync_only_library
class MyCustomAuth(httpx.Auth):
def sync_auth_flow(self, request):
token = sync_only_library.get_token(...)
request.headers["Authorization"] = f"Token {token}"
yield request
async def async_auth_flow(self, request):
raise RuntimeError("Cannot use a sync authentication class with httpx.AsyncClient")