我使用 Python Dash 包构建了一个 Web 应用程序,并将该应用程序部署在 Azure 应用服务上。 Web 应用程序当前通过 Azure 门户的应用程序服务使用 Microsoft 身份提供程序进行身份验证。但是如何获取登录用户的详细信息呢?在本地运行时如何验证我的 Web 应用程序?
我当前的登录流程也是自动重定向,因此没有登录页面,也没有注销按钮/选项。
我希望获得登录用户详细信息这就是我到目前为止所实现的:
authentication.py
import msal
import requests
authority = 'https://login.microsoftonline.com/<tenant-id>'
scope = ["https://graph.microsoft.com/.default"]
client_id = <client-id>
client_secret = <client-secret>
def authentication():
app = msal.ConfidentialClientApplication(
client_id,
authority=authority,
client_credential=client_secret
)
token_response = app.acquire_token_for_client(scopes=scope)
access_token = token_response['access_token']
headers = {
'Authorization': f'Bearer {access_token}'
}
response = requests.get(
'https://graph.microsoft.com/v1.0/me', headers=headers)
user_data = response.json()
print("~~~~~~~~~~~~~~~~~~~~~~")
for key, value in user_data.items():
print(f'{key}: {value}')
print("~~~~~~~~~~~~~~~~~~~~~~")
return
这是我的 app.py 我调用身份验证函数的地方:
from functions.app import authentication
# other imports ...
stylesheets = [
"https://unpkg.com/@mantine/dates@7/styles.css",
"https://unpkg.com/@mantine/code-highlight@7/styles.css",
"https://unpkg.com/@mantine/charts@7/styles.css",
"https://unpkg.com/@mantine/carousel@7/styles.css",
"https://unpkg.com/@mantine/notifications@7/styles.css",
"https://unpkg.com/@mantine/nprogress@7/styles.css",
]
app = Dash(__name__, use_pages=True, external_stylesheets=stylesheets)
server = app.server
authentication.authentication()
app.layout = dmc.MantineProvider(
... # layout stuff
)
if __name__ == '__main__':
app.run_server(debug=True, port=8000)
但这就是我得到的我运行我的 python dash Web 应用程序(python ./app.py):
{'error': {'code': 'BadRequest', 'message': '/me request is only valid with delegated authentication flow.', 'innerError': {'date': '2024-08-05T03:46:12', 'request-id': '<request-id>', 'client-request-id': '<client-request-id>'}}}
当前的代码使用的是客户端凭据流来获取令牌,此方法适用于应用程序本身需要访问资源而不是用户的场景。为了获取登录用户的详细信息,需要使用授权码流或身份验证代码流,该流涉及获取用户同意以访问他们的数据。
以下是使用 MSAL for Python 获取 Azure 应用服务中 Python Dash 应用的登录用户详细信息的步骤:
1. 更新 Azure 应用注册
- 在 Azure 门户中导航到的应用程序注册。
- 在“管理”下,转到“身份验证”。
- 在“平台配置”下,选择“添加平台”。
- 选择“Web”。
-
在“重定向 URI”下,添加的 Web 应用程序的重定向 URI(例如,
https://your-app-name.azurewebsites.net/.auth/login/aad/callback
)。这应该与在 Azure 应用服务的身份验证设置中配置的重定向 URI 相匹配。 - 在“隐式授权”下,选中“ID 令牌”。这是必需的,以便在客户端应用程序中获取令牌。
- 保存的更改。
2. 安装 Flask 库
pip install Flask
3. 更新的 Python 代码
import msal
import requests
from flask import Flask, render_template, session, redirect, url_for, request
import os
app = Flask(__name__)
app.secret_key = os.urandom(24) # 重要提示:出于安全考虑,请使用随机生成的密钥
authority = 'https://login.microsoftonline.com/<tenant-id>'
client_id = '<client-id>'
client_secret = '<client-secret>' # 仅适用于机密客户端应用程序
scope = ["User.Read"] # 请求用户配置文件权限
redirect_uri = 'https://your-app-name.azurewebsites.net/.auth/login/aad/callback' # 更新为的重定向 URI
# 创建 MSAL 机密客户端应用程序
msal_app = msal.ConfidentialClientApplication(
client_id,
authority=authority,
client_credential=client_secret, # 如果是机密客户端应用程序,则取消注释此行
# 如果是公共客户端应用程序,请使用以下内容:
# redirect_uri=redirect_uri,
)
@app.route("/")
def index():
if not session.get("user"):
return redirect(url_for("login"))
return render_template('index.html', user=session['user'])
@app.route("/login")
def login():
session["state"] = str(os.urandom(24)) # 防止 CSRF 攻击
auth_url = msal_app.get_authorization_request_url(
scopes=scope,
state=session["state"],
redirect_uri=redirect_uri,
)
return redirect(auth_url)
@app.route('.auth/login/aad/callback')
def authorized():
if request.args.get('state') != session.get("state"):
return redirect(url_for("index")) # No-OP. Don't log in if state doesn't match
if "error" in request.args: # Authentication/Authorization failure
return render_template("auth_error.html", result=request.args)
if request.args.get('code'):
cache = msal.SerializableTokenCache()
result = msal_app.acquire_token_by_authorization_code(
request.args['code'],
scopes=scope, # Misspelled scope would cause an HTTP 400 error here
redirect_uri=redirect_uri,
token_cache=cache,
)
if "error" in result:
return render_template("auth_error.html", result=result)
session["user"] = result.get("id_token_claims")
session["token_cache"] = cache.serialize()
return redirect(url_for("index"))
@app.route("/logout")
def logout():
session.clear() # This helps prevent CSRF attacks
return redirect(
authority + "/oauth2/v2.0/logout" +
"?post_logout_redirect_uri=" + url_for("index", _external=True)
)
# 此方法从缓存或使用刷新令牌获取令牌
def _get_token_from_cache(scope=None):
cache = msal.SerializableTokenCache()
if session.get("token_cache"):
cache.deserialize(session["token_cache"])
account = msal_app.get_accounts()
if account:
# 尝试从缓存中获取令牌
token_response = msal_app.acquire_token_silent(
scope, account=account, token_cache=cache
)
if token_response:
session["token_cache"] = cache.serialize()
return token_response
@app.route('/graph-call')
def graphcall():
token = _get_token_from_cache(scope=["User.Read"])
if not token:
return redirect(url_for("login"))
graph_data = requests.get( # Use token to call downstream service
'https://graph.microsoft.com/v1.0/me',
headers={'Authorization': 'Bearer ' + token['access_token']},
).json()
return render_template('display.html', result=graph_data)
if __name__ == "__main__":
app.run()
4. 创建模板文件(templates/index.html、templates/display.html、templates/auth_error.html)
templates/index.html
<!DOCTYPE html>
<html>
<head>
<title>MS 身份验证示例</title>
</head>
<body>
{% if user %}
<h1>欢迎,{{ user.name }}!</h1>
<a href="/graph-call">调用 Microsoft Graph</a>
<br>
<a href="/logout">登出</a>
{% else %}
<a href="/login">登录</a>
{% endif %}
</body>
</html>
templates/display.html
<!DOCTYPE html>
<html>
<head>
<title>MS Graph API 调用结果</title>
</head>
<body>
<h1>Microsoft Graph API 调用结果</h1>
<pre>{{ result }}</pre>
</body>
</html>
templates/auth_error.html
<!DOCTYPE html>
<html>
<head>
<title>身份验证错误</title>
</head>
<body>
<h1>身份验证错误</h1>
<p>{{ result }}</p>
</body>
</html>
说明:
-
配置:
使用的应用程序注册值替换
<tenant-id>
、<client-id>
、<client-secret>
和<redirect-uri>
。 -
登录和重定向:
用户访问的应用程序时,
/login
路由会使用 MSAL 库生成登录 URL 并将用户重定向到 Microsoft 身份验证。身份验证后,Microsoft 身份验证将使用授权码将用户重定向到/authorized
路由。 -
获取令牌和用户详细信息:
/authorized
路由使用授权码来获取访问令牌。然后,该令牌用于向 Microsoft Graph API 发出请求,以获取用户的详细信息。 -
注销:
/logout
路由会清除用户会话并将其重定向到 Microsoft 身份验证以清除其单点登录状态。
在本地运行应用程序时进行身份验证:
- 需要将本地运行的应用程序的重定向 URI 添加到 Azure 应用注册中的重定向 URI 列表中。
-
使用
python app.py
启动的应用程序。 - 在 Web 浏览器中访问的应用程序。应该会被重定向到 Microsoft 身份验证以登录。
这个更新后的代码示例使用授权码流来获取用户详细信息,并提供登录和注销功能。它还包括使用
acquire_token_silent
方法从缓存中获取令牌以进行后续 API 调用的机制,从而提供更流畅的用户体验。