使用Ajax调用Microsoft Graph API获取Access Token时出现403 Forbidden错误,但Postman可正常请求
分析403 Forbidden错误的原因及解决方法
我来帮你排查这个问题,先从几个关键点入手:
核心原因1:请求数据格式与Content-Type不匹配
你的代码里存在一个明显的矛盾点:
- 你手动设置了请求头
content-type: application/x-www-form-urlencoded,这是微软OAuth2令牌端点明确要求的参数格式 - 但你同时使用了
FormData,并且设置了contentType: false和processData: false——这会让浏览器自动将请求的Content-Type设为multipart/form-data,和你手动指定的格式直接冲突
微软的令牌端点不接受multipart/form-data格式的请求,这是触发403错误的主要原因。而Postman里你应该是正确选择了x-www-form-urlencoded类型来发送参数,所以能正常获取令牌。
核心原因2:CORS Anywhere公共实例的访问限制
https://cors-anywhere.herokuapp.com/是一个公共的CORS代理服务,它有请求频率限制,而且现在很多浏览器会拦截未验证的请求——如果你之前没访问过它的主页,可能会被临时限制访问,这也可能导致403错误。
解决方法
方法1:修正请求数据格式,匹配x-www-form-urlencoded要求
把FormData换成URLSearchParams,或者直接拼接成符合格式的字符串,同时调整Ajax参数:
const params = new URLSearchParams(); params.append("client_id", "client_id"); params.append("client_secret", "S7D7Q~excS5KjBh9JnPK-afZjTjtALGTKNweP"); params.append("grant_type", "client_credentials"); params.append("scope", "https://graph.microsoft.com/.default"); $(document).ready(function () { requestToken(); }); var token; function requestToken() { $.ajax({ async: true, crossDomain: true, // 移除credentials: "include",client credentials flow不需要携带浏览器凭证 url: "https://cors-anywhere.herokuapp.com/https://login.microsoftonline.com/b262d1f3-4738-400d-ad54-c82cdabb6540/oauth2/v2.0/token", method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, cache: false, // 这里不需要processData和contentType: false,参数格式已经符合要求 data: params, success: function (response) { console.log(response); token = response.access_token; }, error: function(xhr, status, error) { console.log("错误状态:", status); console.log("错误信息:", error); console.log("响应内容:", xhr.responseText); } }); }
方法2:解锁CORS Anywhere公共实例的访问权限
如果你继续使用公共的CORS Anywhere实例,先在浏览器中访问它的主页,点击页面上的"Request temporary access to the demo server"按钮,解锁临时访问权限,然后再运行你的Ajax代码。
更稳妥的方案:自己部署一个私有CORS Anywhere实例,避免公共服务的限流和访问限制。
方法3:额外检查点
- 再次核对
client_id和client_secret的拼写(Postman能正常请求的话这个大概率没问题,但还是确认下) - 务必移除
credentials: "include"——client credentials flow是通过client_id和client_secret验证身份,不需要携带浏览器的Cookie等凭证,这个参数可能会干扰请求
内容的提问来源于stack exchange,提问作者user3755154
相关产品推荐
相关产品推荐

