Azure App Service上Python Dash应用获取微软认证用户信息及本地认证
问题分析与解决方案
你当前使用客户端凭证流(通过acquire_token_for_client获取Token)调用Microsoft Graph的/me接口,该流属于应用级权限,无具体登录用户上下文,而/me接口要求必须使用委托认证流(关联用户上下文),因此返回BadRequest错误。以下是针对性解决步骤:
一、获取登录用户信息的正确方式
Azure App Service的Microsoft Identity Provider(Easy Auth)已自动处理用户登录,会将用户身份信息注入请求头,无需再通过客户端凭证获取Token。
方式1:直接从请求头读取基础用户信息
Easy Auth会在认证请求中添加X-MS-CLIENT-PRINCIPAL等头信息,可直接解析获取用户数据:
from flask import request import base64 import json def get_current_user(): client_principal = request.headers.get('X-MS-CLIENT-PRINCIPAL') if not client_principal: return None # 解码Base64格式的用户主体信息 decoded_data = base64.b64decode(client_principal).decode('utf-8') user_data = json.loads(decoded_data) # 提取核心用户信息 return { 'username': user_data['userDetails'], 'name': user_data['name'], 'email': next(claim['value'] for claim in user_data['claims'] if claim['typ'] == 'email') }
方式2:用用户的Access Token调用Graph接口
若需更详细的用户信息(如部门、头像),可从请求头获取用户专属的Access Token后调用/me接口:
from flask import request import requests def get_full_user_details(): user_access_token = request.headers.get('X-MS-TOKEN-AAD-ACCESS-TOKEN') if not user_access_token: return None headers = {'Authorization': f'Bearer {user_access_token}'} response = requests.get('https://graph.microsoft.com/v1.0/me', headers=headers) response.raise_for_status() return response.json()
二、本地运行时的认证配置
本地运行时无法直接使用Azure App Service的Easy Auth,可通过以下两种方案实现认证:
方案1:基于MSAL Flask实现授权码流
- 安装依赖:
pip install msal flask - 修改
app.py集成认证逻辑:
from flask import Flask, redirect, url_for, session, request import msal from dash import Dash, html import dash_mantine_components as dmc # Azure AD配置 CLIENT_ID = '<你的客户端ID>' CLIENT_SECRET = '<你的客户端密钥>' AUTHORITY = 'https://login.microsoftonline.com/<你的租户ID>' SCOPE = ["User.Read", "openid", "profile"] # 使用委托权限,而非.default # 初始化Flask和Dash应用 server = Flask(__name__) server.secret_key = 'your-secure-secret-key' # 生产环境需替换为安全密钥 app = Dash(__name__, server=server, use_pages=True, external_stylesheets=stylesheets) # 登录路由 @server.route('/login') def login(): msal_app = msal.ConfidentialClientApplication( CLIENT_ID, authority=AUTHORITY, client_credential=CLIENT_SECRET ) auth_url = msal_app.get_authorization_request_url( SCOPE, redirect_uri=url_for('authorized', _external=True) ) return redirect(auth_url) # 授权回调路由 @server.route('/authorized') def authorized(): code = request.args.get('code') msal_app = msal.ConfidentialClientApplication( CLIENT_ID, authority=AUTHORITY, client_credential=CLIENT_SECRET ) token_result = msal_app.acquire_token_by_authorization_code( code, scopes=SCOPE, redirect_uri=url_for('authorized', _external=True) ) if 'access_token' in token_result: session['access_token'] = token_result['access_token'] session['user_info'] = token_result.get('id_token_claims', {}) return redirect('/') return f"登录失败: {token_result.get('error_description')}" # 登出路由 @server.route('/logout') def logout(): session.clear() # 重定向到Azure AD登出页,完成后返回应用首页 logout_url = f"{AUTHORITY}/oauth2/v2.0/logout?post_logout_redirect_uri={url_for('index', _external=True)}" return redirect(logout_url) # 更新Dash布局,添加登录/登出入口 app.layout = dmc.MantineProvider( html.Div([ # 登录/登出导航栏 html.Div([ html.A('登录', href='/login') if 'user_info' not in session else html.A('登出', href='/logout'), html.Span(f"欢迎, {session.get('user_info', {}).get('name', '')}") if 'user_info' in session else '' ], style={'padding': '10px', 'textAlign': 'right'}), # 原有布局内容 dash.page_container ]) ) if __name__ == '__main__': app.run_server(debug=True, port=8000)
方案2:使用Azure App Service本地调试工具
通过Visual Studio Code的Azure扩展或az webapp up命令启动本地调试,工具会模拟Easy Auth的行为,本地运行时也能获取与部署环境一致的请求头信息。
三、添加登录/登出选项
部署到Azure App Service后,可直接使用Easy Auth提供的内置端点:
- 登录:访问
/.auth/login/aad,登录后自动重定向回应用首页 - 登出:访问
/.auth/logout,登出后可在Azure门户的认证设置中配置重定向页面
在Dash布局中添加对应链接即可:
html.Div([ html.A('登录', href='/.auth/login/aad', style={'marginRight': '10px'}), html.A('登出', href='/.auth/logout') ])
关键注意事项
- 确保Azure AD应用注册已配置委托权限(如
User.Read),而非应用权限 - 本地运行时,需在Azure AD应用注册的「重定向URI」中添加本地地址(如
http://localhost:8000/authorized) - 生产环境不要硬编码客户端密钥,应通过Azure App Service的「应用设置」存储敏感信息
内容的提问来源于stack exchange,提问作者Jason Susanto
相关产品推荐
相关产品推荐

