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

Django+React集成Microsoft Graph API的OAuth授权流程实现方法

Django+React集成Microsoft Graph API 邮件能力授权方案

你需要采用 OAuth 2.0 授权码流(机密客户端模式) 实现凭证托管,这个模式下refresh_token全程不暴露给前端,后端可以长期持有凭证代表用户调用接口,是符合你需求的标准实现方案,具体落地步骤如下:

前置配置(Azure AD侧)

  • 注册Azure AD应用,添加两个平台配置:
    • 单页应用平台:配置React前端的路由地址(比如本地调试用http://localhost:3000,生产环境替换为业务域名)
    • Web平台:配置Django后端的授权回调地址(比如本地调试用http://localhost:8000/api/auth/ms-callback),同时生成客户端密钥(Client Secret),这个密钥只存在后端环境变量里,绝对不能发到前端
  • 配置委托权限(不要选应用程序权限,应用权限是无用户上下文的全局权限,不适合代表单个用户操作),添加以下权限:Mail.ReadWrite、Mail.Send、offline_access、openid、profile,内部租户使用可以直接点管理员同意,多租户场景等用户首次授权时手动同意即可
  • 记录三个核心参数:客户端ID(Client ID)、租户ID(Tenant ID,多租户场景填common)、客户端密钥(Client Secret)

Django后端实现

  • 先建存储用户微软凭证的关联表,不要明文存refresh_token,建议用字段级加密:
from django.db import models
from django.conf import settings

class MSUserCredential(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    ms_account_id = models.CharField(max_length=200, unique=True)  # 微软侧用户唯一标识
    access_token = models.TextField()
    refresh_token = models.TextField()  # *生产环境务必加密存储*
    access_token_expire_at = models.DateTimeField()
    create_time = models.DateTimeField(auto_now_add=True)
    update_time = models.DateTimeField(auto_now=True)
  • 写两个核心授权接口:
    1. 授权地址生成接口:前端点击「绑定微软邮箱」时请求该接口,后端生成随机state字符串存在当前用户session中(用于回调时防CSRF校验),拼接微软授权地址返回给前端,授权地址参数固定包含:client_id、redirect_uri(后端回调地址)、response_type=code、scope(前面加的5个权限,空格分隔)、state
    2. 授权回调接口:微软完成用户授权后,会带着code和state参数重定向到这个接口,首先校验请求里的state和session中存储的state是否一致,不一致直接返回403;校验通过后,后端携带code、client_id、client_secret、redirect_uri、grant_type=authorization_code参数,请求微软令牌端点兑换令牌,拿到access_token、refresh_token、expires_in后,计算access_token过期时间,和当前登录用户绑定存入MSUserCredential表,最后重定向回前端的绑定成功页即可
  • 封装通用的Graph调用工具类,自动处理access_token过期刷新逻辑,避免每次调用都手动判断过期:
import requests
from django.utils import timezone
from datetime import timedelta
from django.conf import settings

def get_valid_access_token(credential):
    # 提前5分钟刷新token,避免临界时间请求失败
    if credential.access_token_expire_at - timedelta(minutes=5) > timezone.now():
        return credential.access_token
    # 用refresh_token换发新token
    refresh_payload = {
        "grant_type": "refresh_token",
        "refresh_token": credential.refresh_token,
        "client_id": settings.MS_CLIENT_ID,
        "client_secret": settings.MS_CLIENT_SECRET,
        "scope": "Mail.ReadWrite Mail.Send offline_access openid profile"
    }
    resp = requests.post(
        f"https://login.microsoftonline.com/{settings.MS_TENANT_ID}/oauth2/v2.0/token",
        data=refresh_payload
    )
    resp.raise_for_status()
    token_data = resp.json()
    # 更新数据库中存储的凭证
    credential.access_token = token_data["access_token"]
    credential.refresh_token = token_data["refresh_token"]
    credential.access_token_expire_at = timezone.now() + timedelta(seconds=token_data["expires_in"])
    credential.save()
    return credential.access_token

# 示例:调用发送邮件接口
def send_user_mail(user, subject, content, to_addr_list):
    cred = MSUserCredential.objects.get(user=user)
    token = get_valid_access_token(cred)
    req_headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    }
    mail_body = {
        "message": {
            "subject": subject,
            "body": {"contentType": "Text", "content": content},
            "toRecipients": [{"emailAddress": {"address": addr}} for addr in to_addr_list]
        }
    }
    resp = requests.post(
        "https://graph.microsoft.com/v1.0/me/sendMail",
        headers=req_headers,
        json=mail_body
    )
    resp.raise_for_status()
    return resp.status_code == 202

后续要实现读邮件、管理邮件文件夹等操作,直接用拿到的有效access_token调用对应Graph接口即可。

React前端实现

前端逻辑非常简单,不需要引入复杂的SDK处理令牌:

  • 绑定微软账号按钮点击后,请求后端的授权地址生成接口,拿到地址后直接window.location.href跳转到该地址即可
  • 提前写好绑定成功/失败的路由页,等后端回调完成重定向回来后,给用户展示对应提示
  • 后续需要触发邮件相关操作时,直接请求你自己封装的Django业务接口即可,不需要在前端持有任何微软相关的令牌

常见踩坑点

  • 不要用已经被微软弃用的隐式授权流,安全性差且无法拿到长期有效的refresh_token
  • 必须加offline_access权限,否则拿不到refresh_token,access_token1小时过期后必须让用户重新授权,体验极差
  • state校验不能省略,否则存在CSRF漏洞,可能导致攻击者诱导用户绑定恶意账号
  • 多租户场景下,token兑换成功后要校验返回的租户ID是否在你的允许范围内,避免任意微软账号都能接入你的应用
  • 生产环境必须加密存储refresh_token,避免数据库泄露导致用户邮箱被未授权访问
  • 所有携带client_secret的请求必须在后端发起,绝对不能把client_secret暴露给前端代码

内容的提问来源于stack exchange,提问作者esieboma jeremiah

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 21:31:11