Azure AD SAML 2.0登录后,Python后端如何提取用户Claims?
自研方案:从Azure AD授权码提取用户登录信息
首先明确:URL中的code不是直接包含用户Claims的明文数据,它是Azure AD颁发的授权码,需要后端用它向Azure AD令牌端点发起请求,换取包含用户Claims的ID Token,再从ID Token中解析出用户信息。
步骤1:前端解析Hash参数并传递给后端
浏览器不会把URL Hash(#后的内容)发送给服务器,所以前端需要先提取code和state,通过接口传给后端:
// React登录回调页面组件 const LoginCallback = () => { useEffect(() => { // 解析Hash中的参数 const hashParams = new URLSearchParams(window.location.hash.slice(1)); const code = hashParams.get('code'); const state = hashParams.get('state'); if (code && state) { // 调用后端接口传递参数 fetch('/api/azure-ad/callback', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ code, state }) }) .then(res => res.json()) .then(userData => { // 处理用户信息,比如存入全局状态、跳转首页 localStorage.setItem('user', JSON.stringify(userData)); window.location.href = '/'; }) .catch(err => console.error('登录回调失败:', err)); } }, []); return <div>登录中...</div>; }; export default LoginCallback;
步骤2:后端验证State并换取令牌
后端需要先验证state(防止CSRF攻击,state是前端发起登录时生成的随机值,需提前存入会话),再用code请求Azure AD令牌端点:
import requests import jwt from flask import request, session, jsonify import os # 从环境变量读取配置(生产环境推荐) AZURE_TENANT_ID = os.getenv('AZURE_TENANT_ID') CLIENT_ID = os.getenv('AZURE_CLIENT_ID') CLIENT_SECRET = os.getenv('AZURE_CLIENT_SECRET') REDIRECT_URI = os.getenv('AZURE_REDIRECT_URI') @app.route('/api/azure-ad/callback', methods=['POST']) def azure_ad_callback(): req_data = request.get_json() code = req_data.get('code') state = req_data.get('state') # 验证state一致性,防范CSRF if not state or state != session.get('azure_ad_state'): return jsonify({'error': '无效的state参数'}), 400 # 构建令牌请求参数 token_payload = { 'client_id': CLIENT_ID, 'client_secret': CLIENT_SECRET, 'code': code, 'redirect_uri': REDIRECT_URI, 'grant_type': 'authorization_code' } # 向Azure AD令牌端点发起请求 token_response = requests.post( url=f'https://login.microsoftonline.com/{AZURE_TENANT_ID}/oauth2/v2.0/token', data=token_payload ) token_data = token_response.json() # 处理令牌请求错误 if 'error' in token_data: return jsonify({'error': token_data['error_description']}), 400 # 解析ID Token获取用户Claims(生产环境必须验证签名) id_token = token_data['id_token'] # 生产环境请使用以下方式验证签名并解析: # claims = jwt.decode( # id_token, # audience=CLIENT_ID, # issuer=f'https://login.microsoftonline.com/{AZURE_TENANT_ID}/v2.0', # algorithms=['RS256'] # ) # 测试环境可临时跳过签名验证: claims = jwt.decode(id_token, options={'verify_signature': False}) # 提取核心用户信息 user_info = { '姓名': claims.get('name'), '邮箱': claims.get('email') or claims.get('upn'), '用户ID': claims.get('oid'), '租户ID': claims.get('tid') } # 存储用户会话(示例用Flask session,可替换为JWT或其他会话机制) session['current_user'] = user_info return jsonify(user_info)
关键注意事项
- ID Token签名验证:测试环境可跳过,但生产环境必须验证,否则存在伪造令牌风险。
jwt库会自动从Azure AD获取公钥完成验证。 - 客户端密钥安全:绝对不要硬编码密钥,使用环境变量、密钥管理服务(如Azure Key Vault)存储。
- State生成规则:前端发起登录时需生成随机、唯一的
state并存入会话(如import { nanoid } from 'nanoid'; const state = nanoid(); sessionStorage.setItem('azure_ad_state', state);)。 - 回调URL一致性:Azure AD门户中配置的回调URL必须和代码中
REDIRECT_URI完全一致,包括协议(http/https)、域名、路径。
内容的提问来源于stack exchange,提问作者Daniel F. González
相关产品推荐
相关产品推荐

