如何基于Azure AD实现REST API的程序化访问授权(替代基础认证)
针对Python脚本程序化调用API的Azure AD认证方案
核心可选方案
根据你的场景(禁止资源所有者密码流,需替代基础认证),有两个符合安全最佳实践的方案可选:
方案1:授权码流 + PKCE(适合需代表特定用户调用的脚本)
授权码流+PKCE并非只能用于前端跳转,针对Python脚本可以实现一次性交互授权+后续静默刷新的模式:
Azure AD应用配置
- 注册一个公共客户端应用(控制台/桌面应用类型,无需客户端密钥)
- 为该应用添加目标API的委托权限(若脚本需要用户上下文),并确保权限已获得管理员同意
- 设置重定向URI为
http://localhost:8000(用于本地回调)
脚本实现逻辑
- 首次运行时,自动打开浏览器引导用户完成Azure AD登录,获取授权码
- 用授权码+PKCE参数交换访问令牌和刷新令牌,将刷新令牌本地持久化(比如写入文件)
- 后续脚本运行时,直接读取刷新令牌静默获取新的访问令牌,无需用户交互
Python示例(依赖msal库):
import msal import webbrowser import os # 配置项 CLIENT_ID = "你的公共客户端应用ID" TENANT_ID = "你的Azure AD租户ID" API_SCOPE = ["api://你的API应用ID/你的权限范围"] # 例如 ["api://xxxx/user.read"] REFRESH_TOKEN_FILE = "refresh_token.txt" # 初始化MSAL客户端 app = msal.PublicClientApplication(CLIENT_ID, authority=f"https://login.microsoftonline.com/{TENANT_ID}") # 尝试从缓存或本地文件获取刷新令牌 result = app.acquire_token_silent(API_SCOPE, account=None) if not result and os.path.exists(REFRESH_TOKEN_FILE): with open(REFRESH_TOKEN_FILE, "r") as f: refresh_token = f.read().strip() result = app.acquire_token_by_refresh_token(refresh_token, API_SCOPE) if not result: # 首次授权:发起PKCE流程,打开浏览器 flow = app.initiate_auth_code_flow(API_SCOPE, redirect_uri="http://localhost:8000") webbrowser.open(flow["auth_uri"]) # 手动输入授权码(或用本地HTTP服务器自动捕获回调) auth_code = input("请复制浏览器地址栏中的授权码并输入:") result = app.acquire_token_by_auth_code_flow(flow, auth_code) # 保存刷新令牌到本地 if "refresh_token" in result: with open(REFRESH_TOKEN_FILE, "w") as f: f.write(result["refresh_token"]) if "access_token" in result: # 用访问令牌调用API print(f"获取访问令牌成功,有效期:{result['expires_in']}秒") # 示例:requests.get("你的API地址", headers={"Authorization": f"Bearer {result['access_token']}"}) else: print(f"令牌获取失败:{result.get('error_description', result.get('error'))}")
方案2:客户端凭证流(适合无用户上下文的服务间调用)
如果脚本不需要模拟特定用户,而是以服务身份独立调用API,客户端凭证流是更合适的选择:
Azure AD应用配置
- 注册一个机密客户端应用,生成客户端密钥(或使用证书提升安全性)
- 为该应用添加目标API的应用权限(而非委托权限),并完成管理员同意
- 确保API应用已将权限范围暴露给客户端应用
脚本实现逻辑
- 直接用客户端ID+密钥(或证书)向Azure AD请求访问令牌,全程无用户交互
Python示例:
import msal # 配置项 CLIENT_ID = "你的机密客户端应用ID" CLIENT_SECRET = "你的客户端密钥" TENANT_ID = "你的Azure AD租户ID" API_SCOPE = ["api://你的API应用ID/.default"] # .default代表应用权限的默认范围 # 初始化MSAL客户端 app = msal.ConfidentialClientApplication( CLIENT_ID, authority=f"https://login.microsoftonline.com/{TENANT_ID}", client_credential=CLIENT_SECRET ) # 获取服务身份令牌 result = app.acquire_token_for_client(scopes=API_SCOPE) if "access_token" in result: print(f"服务身份令牌获取成功:{result['access_token']}") # 调用API:requests.get("你的API地址", headers={"Authorization": f"Bearer {result['access_token']}"}) else: print(f"令牌获取失败:{result.get('error_description', result.get('error'))}")
API端的授权适配
不管用哪种方案,API端需要做以下配置来替代原有的LDAP+AD组授权:
- 在Azure AD注册API应用,定义权限范围(scopes)或应用角色(roles)
- 将原AD全局组映射到Azure AD的组或应用角色,在令牌中通过
groups或roles声明传递 - 在API的认证中间件中验证Azure AD颁发的JWT令牌,检查声明中的权限/组信息完成授权
内容的提问来源于stack exchange,提问作者Emnii
相关产品推荐
相关产品推荐

