如何通过Azure获取用户及服务账号Token实现Web API认证?
适配Azure AD的FastAPI认证方案
针对你需要替换LDAP、支持人类用户交互式+Jenkins非交互式认证、无需客户端密钥/明文密码的需求,以下是直接可用的实现方案:
一、人类用户认证(支持curl调用)
用授权码流(无客户端密钥),用户通过浏览器就能获取token,不用依赖MSAL类库:
- 在Azure AD注册一个「公共客户端应用」(无需设置客户端密钥)
- 用户打开浏览器访问Azure AD授权地址,登录后拿到授权码,再用curl交换token:
# 替换占位符为你的租户ID、客户端ID、浏览器获取的授权码 curl -X POST https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token \ -d "client_id={client-id}" \ -d "grant_type=authorization_code" \ -d "code={浏览器拿到的授权码}" \ -d "redirect_uri=http://localhost" \ -d "scope=api://{你的API客户端ID}/access_as_user" - 拿到access token后,直接用curl调用API:
curl -H "Authorization: Bearer {获取到的access-token}" https://你的FastAPI地址/任务接口 - 优势:用户仅需浏览器登录一次,refresh token可自动续期token,全程无需存储密钥或明文密码。
二、Jenkins等服务账号非交互式认证
推荐两种无密钥方案,根据部署环境选择:
方案1:Azure托管身份(适合Azure上的Jenkins)
如果Jenkins运行在Azure VM/AKS/App Service上,直接启用托管身份:
- 给托管身份授予你的API访问权限(在Azure AD的API权限中添加托管身份的权限)
- Jenkins脚本通过Azure元数据服务获取token,完全无需密钥:
# 替换为你的API客户端ID curl -H "Metadata: true" "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=api://{你的API客户端ID}" - 拿到token后和人类用户的调用方式一致即可。
方案2:联邦身份凭据(适合非Azure环境的Jenkins)
如果Jenkins不在Azure上,用联邦身份凭据实现无密钥认证:
- 在Azure AD注册服务主体,添加联邦身份凭据,关联Jenkins的OIDC提供者(比如Jenkins自身的OIDC服务)
- Jenkins用自身生成的OIDC令牌向Azure AD交换access token,无需存储任何密钥:
# 替换占位符 curl -X POST https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token \ -d "client_id={服务主体客户端ID}" \ -d "grant_type=client_credentials" \ -d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \ -d "client_assertion={Jenkins生成的OIDC令牌}" \ -d "scope=api://{你的API客户端ID}/access_as_service" - 优势:无需维护任何密钥或证书,自动化流程完全无交互。
三、FastAPI的Token验证代码
直接调用Azure AD的JWKS端点验证token合法性,仅依赖基础库:
from fastapi import FastAPI, Depends, HTTPException from fastapi.security import OAuth2PasswordBearer from jose import JWTError, jwt import requests app = FastAPI() oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") # 替换为你的配置 TENANT_ID = "你的租户ID" API_CLIENT_ID = "你的API客户端ID" JWKS_URL = f"https://login.microsoftonline.com/{TENANT_ID}/discovery/v2.0/keys" async def validate_azure_token(token: str = Depends(oauth2_scheme)): try: # 从Azure获取JWKS密钥 jwks_response = requests.get(JWKS_URL) jwks_response.raise_for_status() jwks = jwks_response.json() # 匹配token的签名密钥 header = jwt.get_unverified_header(token) signing_key = next(k for k in jwks["keys"] if k["kid"] == header["kid"]) # 验证token的签名、受众、颁发者 payload = jwt.decode( token, signing_key, algorithms=["RS256"], audience=API_CLIENT_ID, issuer=f"https://login.microsoftonline.com/{TENANT_ID}/v2.0" ) return payload except (JWTError, StopIteration, requests.exceptions.RequestException): raise HTTPException(status_code=401, detail="无效的token") # 受保护的API示例 @app.get("/run-task") async def run_protected_task(user_info: dict = Depends(validate_azure_token)): return {"status": "success", "executed_by": user_info.get("name", "service-account")}
关键注意点
- 给API设置不同的权限范围(比如
access_as_user和access_as_service),区分人类用户和服务账号的操作权限 - 所有token的生命周期由Azure AD管理,无需自行存储token到数据库,过期可自动续期
- 联邦身份凭据是微软推荐的无密钥认证方案,完全符合企业安全政策
内容的提问来源于stack exchange,提问作者khoshahmad
相关产品推荐
相关产品推荐

