Streamlit应用中Azure AD认证网络访问异常问题排查
问题分析:Streamlit Azure AD认证跨局域网访问异常
问题现象
- 本地访问
http://localhost:8501时,点击登录按钮可正常跳转至Microsoft认证页面,完成登录后返回令牌字典。 - 局域网内其他用户通过网络URL访问应用时,点击登录按钮后,Microsoft认证弹窗会在服务器端机器打开,而非用户本地机器。
原始代码
import streamlit as st import msal import webbrowser import requests from selenium.webdriver.support.ui import WebDriverWait from selenium import webdriver from selenium.webdriver.support import expected_conditions as EC import urllib client_id = "xxxx" tenant_id = "xxxx" client_secret = "xxxx" redirect_uri = "http://localhost:8501/" scopes = ["https://graph.microsoft.com/.default"] authority = f"https://login.microsoftonline.com/{tenant_id}" endpoint = "https://graph.microsoft.com/v1.0/me" app = msal.ConfidentialClientApplication( client_id, client_credential=client_secret, authority=authority, verify=False ) def get_token_from_cache(): accounts = app.get_accounts() if not accounts: return None result = app.acquire_token_silent(scopes, account=accounts[0]) if "access_token" in result: return result["access_token"] else: return None def login(): flow = app.initiate_auth_code_flow( scopes=scopes) if "auth_uri" not in flow: return st.write("Failed with token") auth_uri = flow["auth_uri"] browser = webdriver.Chrome() browser.get(auth_uri) WebDriverWait(browser, 200).until( EC.url_contains(redirect_uri)) redirected_url = browser.current_url url = urllib.parse.urlparse(redirected_url) # parse the query string to get a dictionary of {key: value} query_params = dict(urllib.parse.parse_qsl(url.query)) #code = query_params.get('code')[0] result = app.acquire_token_by_auth_code_flow(flow,query_params, scopes=scopes) browser.quit() return result if st.button("Login"): token = get_token_from_cache() if not token: token = login() st.write(st.experimental_get_query_params()) if token: st.write("Logged in successfully!") st.write(token) else: st.write("Failed to login")
问题根源确认
你的猜测完全正确:核心问题出在webdriver.Chrome()这段代码。Streamlit的后端逻辑运行在服务器机器上,当远程用户点击登录按钮时,这段代码会在服务器端启动Chrome浏览器,而非用户本地的浏览器,导致整个认证流程在服务器端执行,出现弹窗错位的问题。
解决方案:重构OAuth2授权码流程
要让用户在本地浏览器完成认证,需改用标准的OAuth2授权码流程,去掉服务器端启动浏览器的逻辑,改为引导用户在本地完成认证,再回传授权码到服务器获取令牌。
修改步骤及代码
- 更新重定向URI:将
redirect_uri改为服务器的网络地址(如http://你的服务器IP:8501/),同时在Azure AD应用注册后台同步更新该重定向URI。 - 移除Selenium依赖:删掉所有与
webdriver相关的代码,改用Streamlit会话状态管理授权流程上下文。 - 引导用户本地认证:通过页面链接让用户在本地浏览器打开认证页面,完成后回调到应用获取令牌。
修改后的代码:
import streamlit as st import msal import urllib.parse client_id = "xxxx" tenant_id = "xxxx" client_secret = "xxxx" # 替换为服务器的网络地址,需和Azure AD应用注册中的重定向URI一致 redirect_uri = "http://你的服务器IP:8501/" scopes = ["https://graph.microsoft.com/.default"] authority = f"https://login.microsoftonline.com/{tenant_id}" app = msal.ConfidentialClientApplication( client_id, client_credential=client_secret, authority=authority, verify=False ) def get_token_from_cache(): accounts = app.get_accounts() if not accounts: return None result = app.acquire_token_silent(scopes, account=accounts[0]) return result.get("access_token") # 处理认证回调:检查URL中的授权码 query_params = st.experimental_get_query_params() if "code" in query_params and "auth_flow" in st.session_state: # 使用授权码交换令牌 result = app.acquire_token_by_auth_code_flow( st.session_state["auth_flow"], query_params, scopes=scopes ) if "access_token" in result: st.session_state["access_token"] = result["access_token"] # 清除URL中的查询参数,避免重复处理 st.experimental_set_query_params() else: st.error(f"登录失败: {result.get('error_description', '未知错误')}") # 主页面逻辑 if "access_token" not in st.session_state: if st.button("登录"): # 初始化授权码流程 flow = app.initiate_auth_code_flow(scopes=scopes, redirect_uri=redirect_uri) st.session_state["auth_flow"] = flow # 引导用户在本地浏览器打开认证链接 st.markdown(f"请点击[这里]({flow['auth_uri']})进行登录", unsafe_allow_html=True) else: st.success("登录成功!") st.write("令牌信息:", st.session_state["access_token"]) if st.button("退出登录"): # 清理会话状态 del st.session_state["access_token"] if "auth_flow" in st.session_state: del st.session_state["auth_flow"] st.experimental_rerun()
核心优化点
- 去掉服务器端启动浏览器的逻辑,改为用户本地浏览器完成认证。
- 用Streamlit的
session_state保存授权流程上下文,避免认证过程中丢失状态。 - 处理认证后的回调参数,在服务器端安全完成令牌交换。
- 增加退出登录功能,完善会话管理。
内容的提问来源于stack exchange,提问作者Vic
相关产品推荐
相关产品推荐

