You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Unity集成OAuth2.0调用Dynamics 365遇401及授权码获取问题求助

解决Unity连接Microsoft Dynamics 365的OAuth2.0 401未授权问题

先帮你理清核心问题,再针对性修复代码和配置上的疏漏:

一、关于无交互授权的疑惑

你控制台用Microsoft.IdentityModel.Clients.ActiveDirectory包实现无交互授权,走的是客户端凭证流(Client Credentials Flow)——这本来就是服务对服务场景的无用户交互模式,完全不需要走需要用户登录的授权码流(也就是你调用/authorize接口的方式)。所以咱们不用纠结授权码流,继续用客户端凭证流就行,问题出在配置和代码细节上。

二、401未授权的核心原因及修复

1. Scope配置完全错误

你的代码里用的scopehttps://graph.microsoft.com/.default,这是给Microsoft Graph API用的,但你要访问的是Dynamics 365 Finance and Operations(从cloudax.dynamics.com域名能看出来),对应的scope必须是你的Dynamics实例URL加上/.default,比如:

https://[ENVIRONMENT].cloudax.dynamics.com/.default

这个scope表示使用你在Azure AD应用注册里配置的所有静态应用权限。

2. 请求头设置顺序搞反了

看你调用API的代码,居然是先发送请求,再设置Authorization请求头?这肯定不行!必须先设置好请求头,再发送请求。另外你在获取token后直接调用result(token)的逻辑也有问题,应该等token拿到后,先配置请求头再执行发送操作。

修正后的GetData方法:

private IEnumerator GetData(Action<string> result) {
    string token = null;
    // 先等待token获取完成,确保拿到有效令牌
    yield return GetAccessToken((tokenResult) => { token = tokenResult; });

    if (string.IsNullOrEmpty(token)) {
        result("获取Token失败");
        yield break;
    }

    Dictionary<string, string> content = new Dictionary<string, string>();
    content.Add("CustomerGroupId", "10");

    UnityWebRequest www = UnityWebRequest.Post("https://[ENVIRONMENT].cloudax.dynamics.com/data/TestEntity", content);
    // 先设置授权请求头,再发送请求
    www.SetRequestHeader("Authorization", "Bearer " + token);
    // 注意:Unity新版本推荐用SendWebRequest替代Send
    yield return www.SendWebRequest();

    if (!www.isError) {
        string resultContent = www.downloadHandler.text;
        // 处理返回数据
        result(resultContent);
    } else {
        result("请求失败:" + www.error);
    }
}

3. Azure AD应用权限配置遗漏

确保你在Azure AD注册的应用已经添加了Dynamics 365的应用权限(不是委托权限,客户端凭证流只认应用权限),并且已经由管理员授予同意:

  • 登录Azure门户,找到你的应用注册
  • 进入「API权限」页面,点击「添加权限」
  • 搜索并选择「Dynamics 365 Finance and Operations」
  • 添加符合你需求的应用权限(比如Financials.ReadWrite.All
  • 点击「授予管理员同意」按钮完成权限配置

三、Token获取代码的优化建议

你的GetAccessToken方法整体没问题,但可以明确设置Content-Type,让请求更规范;另外加上错误日志方便排查:

private IEnumerator GetAccessToken(Action<string> result) {
    Dictionary<string, string> content = new Dictionary<string, string>();
    // 替换成你的Dynamics实例Scope
    content.Add("scope", "https://[ENVIRONMENT].cloudax.dynamics.com/.default");
    content.Add("grant_type", "client_credentials");
    content.Add("client_id", "xxxxx");
    content.Add("client_secret", "xxxx");

    UnityWebRequest www = UnityWebRequest.Post("https://login.microsoftonline.com/[TENANTID]/oauth2/v2.0/token", content);
    // 明确设置Content-Type,符合OAuth2接口要求
    www.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded");

    yield return www.SendWebRequest();

    if (!www.isError) {
        string resultContent = www.downloadHandler.text;
        TokenClassName json = JsonUtility.FromJson<TokenClassName>(resultContent);
        result(json.access_token);
    } else {
        result("");
        Debug.LogError("获取Token失败:" + www.error + " | 响应内容:" + www.downloadHandler.text);
    }
}

另外确保你的TokenClassName序列化类结构正确:

[System.Serializable]
public class TokenClassName {
    public string access_token;
    public string token_type;
    public int expires_in;
    // 其他字段如需要可添加
}

四、额外排查步骤

如果还是遇到401,可以按以下步骤排查:

  • 用Postman测试Token获取和API调用,确认Token有效且权限配置正确
  • 检查Dynamics 365实例是否允许该Azure AD应用访问(部分环境需要额外的白名单配置)
  • 用jwt.io解析拿到的Token,确认aud(受众)字段是否匹配你的Dynamics实例URL
  • 查看Unity的网络日志,确认请求头里的Bearer Token是否正确发送

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

火山引擎 最新活动