You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Azure AD公钥端点是否有调用限制?Python/FastAPI咨询

Azure AD公钥端点调用限制与密钥缓存优化方案

一、公钥端点调用限制说明

直接调用https://login.microsoftonline.com//discovery/keys?appid=端点不会轻易被限制或封禁:

  • 微软对这类OpenID发现端点的默认速率限制远高于3000次/小时(通常单租户端点的限制是每分钟数百次,换算后每小时可达数万次),你的请求量完全在安全范围内。
  • 但不建议每次请求都调用该端点,一是没必要浪费网络资源,二是微软官方明确推荐缓存密钥以提升性能和可靠性。

二、现有问题的优化方案

针对你遇到的密钥未通知变更导致异常的问题,结合Python/FastAPI技术栈,推荐以下几种方案:

1. 本地内存缓存+定时刷新(单实例或无状态多实例首选)

  • 实现思路:
    • 在FastAPI应用启动时,首次拉取密钥并缓存到本地内存(比如用cachetools库的TTLCache设置过期时间)。
    • 用定时任务(比如APScheduler)每隔一段时间(比如1小时)主动拉取最新密钥,更新本地缓存。
    • 每次请求验证JWT时,直接从本地缓存读取密钥,无需额外网络请求。
  • 优势:性能最优,完全避免外部调用开销;密钥更新及时,能应对微软的密钥轮换。
  • 代码示例片段:
    from fastapi import FastAPI
    from cachetools import TTLCache
    import requests
    from apscheduler.schedulers.asyncio import AsyncIOScheduler
    
    app = FastAPI()
    # 缓存1小时,最多存10条密钥(足够应对Azure AD的密钥轮换)
    key_cache = TTLCache(maxsize=10, ttl=3600)
    APP_ID = "你的应用ID"
    KEY_ENDPOINT = f"https://login.microsoftonline.com//discovery/keys?appid={APP_ID}"
    
    def fetch_and_update_keys():
        response = requests.get(KEY_ENDPOINT)
        response.raise_for_status()
        keys = response.json()
        key_cache.clear()
        for key in keys.get("keys", []):
            key_cache[key["kid"]] = key
    
    # 启动时初始化缓存
    fetch_and_update_keys()
    
    # 启动定时任务,每小时更新一次
    scheduler = AsyncIOScheduler()
    scheduler.add_job(fetch_and_update_keys, "interval", hours=1)
    scheduler.start()
    
    # 在JWT验证依赖中使用缓存的密钥
    async def get_jwt_key(kid: str):
        return key_cache.get(kid)
    

2. 分布式缓存(多实例部署场景)

如果你的API是多实例部署,本地缓存无法同步更新,推荐用Redis做分布式缓存:

  • 实现思路:
    • 用定时任务拉取最新密钥,写入Redis并设置过期时间(比如1小时)。
    • 每个API实例从Redis读取密钥,无需各自维护缓存。
  • 优势:多实例间密钥状态一致,避免单个实例缓存过期不及时的问题;性能比S3更优,Redis是内存级读取。

3. S3缓存优化(若坚持使用S3)

如果一定要用S3存储密钥,可以做以下优化减少性能影响:

  • 在API实例本地加一层短期内存缓存(比如5分钟),减少S3调用频率。
  • 给S3中的密钥文件设置合适的Cache-Control响应头(比如max-age=300),利用客户端缓存。
  • 用S3的ETag或Last-Modified头做条件请求,只在密钥更新时才下载新文件。

三、额外注意事项

  • Azure AD的密钥轮换通常是提前发布新密钥,旧密钥会保留一段时间再失效,所以定时刷新间隔设置为1小时完全足够覆盖轮换周期。
  • 验证JWT时,如果遇到缓存中没有对应kid的密钥,可临时触发一次密钥拉取并更新缓存,避免直接返回错误。

内容的提问来源于stack exchange,提问作者Rodrigo Petrizzo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 05:03:14