> This page is for SDK, version Current (default).
> For other versions, use one of these documentation indexes:
> - Current (default): https://buildwithfern.com/learn/sdks/current/llms.txt
> - Legacy: https://buildwithfern.com/learn/sdks/legacy/llms.txt

> For clean Markdown content of this page, append .md to this URL. For the complete documentation index, see https://buildwithfern.com/learn/llms.txt.

# 动态身份验证

> 在 Python SDK 中使用方法重写来实现动态身份验证模式，如短期 JWT 签名。

您的 API 可能需要动态身份验证，其中凭据需要为每个请求生成或刷新，例如签名短期 JWT 或轮换令牌。Python SDK 通过方法重写支持这种模式。

## 方法重写模式

对于 Python SDK，您可以通过扩展生成的客户端并重写方法来注入身份验证逻辑来实现动态身份验证。

### 示例：短期 JWT 签名

以下是如何为 Python SDK 实现 JWT 签名：

### 创建包装器客户端

创建一个自定义客户端类，扩展生成的 Fern 客户端并添加 JWT 签名逻辑：

**`src/wrapper/client.py`**

```python title="src/wrapper/client.py"
from .client import PlantStoreClient as FernClient
import jwt
import time
from typing import Optional, Dict, Any

class PlantStoreClient(FernClient):
    def __init__(self, *, private_key: str, environment: str):
        super().__init__(environment=environment)
        self._private_key = private_key
    
    def _generate_jwt(self) -> str:
        """生成有效期为 15 秒的短期 JWT 令牌"""
        now = int(time.time())
        payload = {
            "iat": now,
            "exp": now + 15,
        }
        return jwt.encode(payload, self._private_key, algorithm="RS256")
    
    def _with_jwt(self, request_options: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        """将 JWT 注入请求选项"""
        token = self._generate_jwt()
        options = request_options or {}
        headers = options.get("headers", {})
        headers["Authorization"] = f"Bearer {token}"
        options["headers"] = headers
        return options
    
    def get_plant(self, plant_id: str, *, request_options: Optional[Dict[str, Any]] = None):
        return super().plants.get(plant_id, request_options=self._with_jwt(request_options))
    
    def create_plant(self, request: Any, *, request_options: Optional[Dict[str, Any]] = None):
        return super().plants.create(request, request_options=self._with_jwt(request_options))
    
    # 根据需要重写其他方法...
```

### 导出包装器客户端

更新您的 `__init__.py` 以导出包装器而不是生成的客户端：

**`src/__init__.py`**

```python title="src/__init__.py"
from .wrapper.client import PlantStoreClient

__all__ = ["PlantStoreClient"]
```

### 添加到 `.fernignore`

保护您的自定义代码免于被覆盖：

**`.fernignore`**

```diff title=".fernignore"
+ src/wrapper
+ src/__init__.py
```

### 使用客户端

用户可以使用具有自动 JWT 签名的客户端：

```python
from plant_store_sdk import PlantStoreClient
import os

client = PlantStoreClient(
    private_key=os.environ["PRIVATE_KEY"],
    environment="https://api.plantstore.com"
)

# JWT 会自动为每个请求签名和注入
plant = client.get_plant("monstera-123")
new_plant = client.create_plant({
    "name": "Fiddle Leaf Fig",
    "species": "Ficus lyrata"
})
```

> **Note**
>
> 这种方法需要单独重写每个方法。您需要重写所有进行 API 调用的方法，以确保 JWT 身份验证在您的 SDK 中一致应用。

## 其他身份验证模式

这种相同的模式适用于其他动态身份验证场景：

* **OAuth 令牌刷新**：在每个请求之前自动刷新过期的访问令牌
* **HMAC（基于哈希的消息认证码）签名**：基于请求内容使用 HMAC 签名对请求进行签名
* **轮换 API 密钥**：基于速率限制在多个 API 密钥之间切换
* **基于时间的令牌**：生成包含时间戳或随机数的令牌

## 重要注意事项

### 安全注意事项

* **安全密钥存储**：绝不要硬编码私钥；使用环境变量或安全的密钥管理系统
* **避免双重身份验证**：如果您的 API 在 Fern 定义中已经使用了 bearer token 身份验证，请注意不要覆盖现有的 `Authorization` 标头。考虑使用不同的标头名称或有条件地设置标头

### 性能

* **令牌记忆化**：考虑缓存令牌以避免在每个请求上重新生成它们。您可以添加一个简单的缓存，在令牌过期之前刷新令牌
* **宽限期**：在令牌过期前稍早刷新令牌（例如，提前 2 秒）以避免令牌在请求处理期间过期的边缘情况

### 标头合并

* **保留现有标头**：在注入身份验证标头时，始终与现有标头合并，以避免覆盖 SDK 或用户设置的标头
* **请求选项**：Python SDK 接受 `request_options` 作为关键字参数，可以包含自定义标头

### 时间同步

* **时钟偏移**：注意客户端和服务器之间潜在的时钟偏移。考虑为您的令牌过期检查添加容差
* **时间戳精度**：为 `iat` 和 `exp` 声明使用 Unix 时间戳（自纪元以来的秒数）以匹配 JWT 标准

## 最佳实践

* **重写所有方法**：确保您重写所有 API 方法以在您的 SDK 中保持一致的身份验证
* **适当缓存令牌**：在安全性（更短的令牌生命周期）和性能（较少的重新生成频率）之间取得平衡
* **优雅地处理错误**：为身份验证失败和令牌刷新错误实现重试逻辑
* **彻底测试**：确保您的包装器处理所有边缘情况，包括并发请求、令牌过期和网络故障
* **监控令牌使用**：记录令牌生成和刷新事件以帮助调试生产环境中的身份验证问题

## 另请参阅

* [添加自定义代码](/learn/zh/sdks/generators/python/custom-code) - Python 特定的自定义指南
* [Python 配置](/learn/zh/sdks/generators/python/configuration) - 配置选项的完整列表