使用MSAL与Streamlit获取Access Token失败,报错AADSTS9002327求助
问题排查与解决方案
错误根源分析
AADSTS9002327错误的核心原因是Azure Entra ID应用注册类型与你的应用架构不匹配:你注册了「单页应用(SPA)」类型,但Streamlit是服务器端应用,需要用「Web应用/API」类型;同时你误用了MSAL的PublicClientApplication(面向桌面/移动等公共客户端),服务器端应用需使用ConfidentialClientApplication。
步骤1:修正Azure Entra ID应用注册
- 进入Azure门户 -> 你的Entra ID应用注册
- 删除原SPA类型的重定向URI,添加Web类型的重定向URI:
http://localhost:8503http://localhost:8501
- 切换到「证书和密码」页面,创建一个客户端密码(记录下密码值,只显示一次)
- 确保已添加所需的API权限(比如Microsoft Graph的
User.Read权限,若需要全局访问需完成管理员同意)
步骤2:修改Streamlit代码
替换原代码为以下版本,核心变化是改用ConfidentialClientApplication,并实现服务器端标准的授权码流:
import streamlit as st from msal import ConfidentialClientApplication import requests # 配置参数 CLIENT_ID = "<你的客户端ID>" TENANT_ID = "<你的租户ID>" CLIENT_SECRET = "<你创建的客户端密码>" AUTHORITY = f"https://login.microsoftonline.com/{TENANT_ID}" REDIRECT_URI = "http://localhost:8503" # 可替换为localhost:8501 SCOPES = ["https://graph.microsoft.com/User.Read"] # 明确Microsoft Graph权限的完整路径 # 初始化MSAL保密客户端应用 app = ConfidentialClientApplication( CLIENT_ID, authority=AUTHORITY, client_credential=CLIENT_SECRET ) def generate_auth_url(): # 生成Azure认证跳转URL return app.get_authorization_request_url( SCOPES, redirect_uri=REDIRECT_URI, prompt="select_account" ) def exchange_code_for_token(auth_code): # 用授权码兑换access_token return app.acquire_token_by_authorization_code( auth_code, scopes=SCOPES, redirect_uri=REDIRECT_URI ) # Streamlit UI逻辑 st.title("Azure Entra ID 登录验证") # 处理认证回调:检查URL中的授权码参数 query_params = st.experimental_get_query_params() if "code" in query_params: auth_code = query_params["code"][0] token_result = exchange_code_for_token(auth_code) if "access_token" in token_result: st.session_state.token = token_result["access_token"] # 清除URL中的code参数,避免重复处理 st.experimental_set_query_params() st.success("登录成功!") # 可选:调用Microsoft Graph API获取用户信息验证token有效性 headers = {"Authorization": f"Bearer {token_result['access_token']}"} user_info = requests.get("https://graph.microsoft.com/v1.0/me", headers=headers).json() st.write(f"欢迎,{user_info['displayName']}") else: st.error(f"Token获取失败: {token_result.get('error_description', '未知错误')}") # 显示登录入口或已登录状态 if "token" not in st.session_state: auth_url = generate_auth_url() st.markdown(f"[点击登录Azure Entra ID]({auth_url})", unsafe_allow_html=True) else: st.write("Access Token:", st.session_state.token) st.write(st.session_state)
关键修改点说明
- 改用
ConfidentialClientApplication:服务器端应用属于保密客户端,需要客户端密码完成身份验证,规避SPA的跨域限制 - 使用授权码流:符合Web应用的标准OAuth2流程,是服务器端应用获取token的正确方式
- 明确Scopes格式:Microsoft Graph的权限需要完整URL路径(如
https://graph.microsoft.com/User.Read),而非仅User.Read - 处理回调参数:通过
st.experimental_get_query_params()捕获授权码,完成token兑换后清除URL参数避免重复触发
额外注意事项
- 确保Streamlit运行在你配置的重定向URI端口(8503或8501)
- 客户端密码不要硬编码在代码中,生产环境建议用环境变量管理
- 若需访问其他业务API,需在Azure应用注册中添加对应权限并完成同意操作
内容的提问来源于stack exchange,提问作者Tony wang
相关产品推荐
相关产品推荐

