You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 18:35:13