JQuery请求REST服务返回401 Unauthorized问题求助
我最近给学校开发了一款小型音频应用,部署在Heroku上,结果碰到个棘手的问题:用jQuery写的getPlaylist函数调用API获取歌曲时,一直返回401未授权状态码,但直接在浏览器地址栏访问API控制器地址却完全正常。数据库用的是ClearDB插件,MySQL Workbench连接也没毛病,这就让人很困惑了。
先贴一下我的AJAX调用代码:
getPlaylist : function() { $.ajax({ type : 'GET', url : basePath + '/songs', dataType : 'json', contentType : 'application/json', success : function(songs) { $('#radioPlaylist').empty(); for (let song of songs) { $('#radioPlaylist').append( '<div class="form-group">' + '<input type="text" class="form-control songText" value="#'+ song.id + ' '+ song.title + '" disabled="disabled">' + '</div>'); } } }); }
另外我也抓了请求头和响应头的信息:
响应头:Cache-Control: no-cache, no-store, max-age=0, must-revalidate; Connection: keep-alive; Content-Type:application/json;charset=UTF-8; Date:at, 09 Mar 2019 16:07:20 GMT; Expires:0; Pragma:no-cache; Server:Cowboy; Strict-Transport-Security:max-age=31536000 ; includeSubDomains; Transfer-Encoding:chunked; Via :1.1 vegur; X-Content-Type-Options :nosniff; X-Frame-Options:DENY; X-Xss-Protection:1; mode=block
请求头:Accept:application/json, text/javascript, /; q=0.01; Accept-Encoding :gzip, deflate, br; Accept-Language: en-GB,en;q=0.5; Connection: keep-alive; Host:portfolio-st.herokuapp.com; Referer:https://portfolio-st.herokuapp.com/; User-Agent:Mozilla/5.0 (Windows NT 10.0; …) Gecko/20100101 Firefox/65.0; X-Requested-With:XMLHttpRequest
可能的原因和解决步骤
1. 会话Cookie未被AJAX请求携带
直接在浏览器访问API时,浏览器会自动带上当前会话的Cookie,但AJAX请求有时候可能因为配置问题没带上——特别是如果后端依赖会话认证(比如Spring Security这类框架),没有Cookie的话后端就会认为你未授权。
你可以先打开浏览器开发者工具(F12),切换到Network标签,找到这个/songs的AJAX请求,查看Request Headers里有没有Cookie字段。如果没有,试试在AJAX配置里加上xhrFields: { withCredentials: true },强制带上凭证:
getPlaylist : function() { $.ajax({ type : 'GET', url : basePath + '/songs', dataType : 'json', contentType : 'application/json', xhrFields: { withCredentials: true // 新增配置 }, success : function(songs) { $('#radioPlaylist').empty(); for (let song of songs) { $('#radioPlaylist').append( '<div class="form-group">' + '<input type="text" class="form-control songText" value="#'+ song.id + ' '+ song.title + '" disabled="disabled">' + '</div>'); } } }); }
2. CSRF令牌验证失败
如果后端启用了CSRF防护(比如Spring Security默认开启),AJAX请求需要带上CSRF令牌,而直接访问浏览器的GET请求可能不会触发CSRF验证(部分框架只对POST/PUT/DELETE等修改请求验证,但有些会强制所有请求都验证)。
解决方法是在页面里添加CSRF令牌的meta标签(如果后端是Spring Boot,通常会自动生成,或者你手动添加):
<meta name="_csrf" content="${_csrf.token}"/> <meta name="_csrf_header" content="${_csrf.headerName}"/>
然后在AJAX请求里带上这个令牌:
getPlaylist : function() { var token = $("meta[name='_csrf']").attr("content"); var header = $("meta[name='_csrf_header']").attr("content"); $.ajax({ type : 'GET', url : basePath + '/songs', dataType : 'json', contentType : 'application/json', beforeSend: function(xhr) { xhr.setRequestHeader(header, token); // 新增令牌头 }, success : function(songs) { $('#radioPlaylist').empty(); for (let song of songs) { $('#radioPlaylist').append( '<div class="form-group">' + '<input type="text" class="form-control songText" value="#'+ song.id + ' '+ song.title + '" disabled="disabled">' + '</div>'); } } }); }
3. 检查basePath是否正确
虽然直接访问API地址正常,但还是确认下basePath的值是不是https://portfolio-st.herokuapp.com/api,如果拼接后的URL有误,也可能导致奇怪的问题。
4. 查看Heroku后端日志
最直接的方法是看后端到底为什么返回401,执行heroku logs --tail命令查看Heroku的实时日志,里面会有详细的错误原因,比如是会话不存在、权限不足还是CSRF验证失败,能帮你快速定位问题。
内容的提问来源于stack exchange,提问作者Stefan

