CROS實現跨域時授權問題(401錯誤)的解決
如果我們訪問的資源是不需要授權的,也就是在HTTP請求頭中不包含 authentication 頭那么以上做法就足夠了。但是如果該資源是需要權限驗證的,那么這個時候跨域請求的預檢測 option 請求,由于不會攜帶身份信息而被拒絕 。瀏覽器會報出401錯誤。
前幾天的文章 Spring通過CROS協議解決跨域問題 中提到了如何解決跨域問題的基本思路,解決了跨域請求時瀏覽器403錯誤。
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. The response had HTTP status code 403
401錯誤信息如下:
Failed to load resource: the server responded with a status of 401 (Unauthorized) XMLHttpRequest cannot load http://localhost/api/test. Response for preflight has invalid HTTP status code 401
既然知道了問題的原因,答案也就很容易得出: 對需要進行跨域請求的資源(api),當服務端檢測到是 OPTONS 請求時候統統放行,給出HTTP.OK(200)的狀態和必要的響應頭,哪怕它是不帶身份信息的 。
這個問題既可以通過編寫對應的后端代碼實現,也可以通過設置服務器配置文件實現。也就是如何設置響應頭和返回200狀態碼的辦法了。
Spring+Shrio的解決方案
shiro中可以在自己實現的身份驗證filter中加入以下代碼:
@Override protected boolean preHandle(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; if(request.getMethod().equals(RequestMethod.OPTIONS.name())) { response.setStatus(HttpStatus.OK.value()); return false; } return super.preHandle(request, response); }
shiro中AccessControlFilter提供了訪問控制的基礎功能;比如是否允許訪問/當訪問拒絕時如何處理等,也是我們一般自定義權限驗證時候的一個父類,我們通過重寫他的 onPreHandle 方法判斷是否是 option 請求,如果是則設置相應狀態,(響應頭已經在之前文章中通過filter配置過了)返回false表示該攔截器實例已經處理了,將直接返回即可。
Tomcat配置
需要修改tomcat的全局web.xml文件在 CATALINA_HOME/conf 下,加入以下配置。
<filter> <filter-name>CorsFilter</filter-name> <filter-class>org.apache.catalina.filters.CorsFilter</filter-class> </filter> <filter-mapping> <filter-name>CorsFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
Nginx配置
add_header 'Access-Control-Allow-Methods' 'GET,OPTIONS,PUT,DELETE' always; add_header 'Access-Control-Allow-Credentials' 'true' always; add_header 'Access-Control-Allow-Origin' '$http_origin' always; add_header 'Access-Control-Allow-Headers' 'Authorization,DNT,User-Agent,Keep-Alive,Content-Type,accept,origin,X-Requested-With' always; if ($request_method = OPTIONS ) { return 200; }
Apache配置
Header always set Access-Control-Allow-Origin "http://waffle" Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS" Header always set Access-Control-Allow-Credentials "true" Header always set Access-Control-Allow-Headers "Authorization,DNT,User-Agent,Keep-Alive,Content-Type,accept,origin,X-Requested-With" RewriteCond %{REQUEST_METHOD} OPTIONS RewriteRule ^(.*)$ $1 [R=200,L]
js請求示例
請求時候需要加上 Authorization 和 Content-Type 頭。
$http({ method: 'POST', url: scope.webdav.url, withCredentials: true, headers: { Authorization: 'Basic ' + btoa(user + ':' + password), 'Content-Type': 'application/vnd.google-earth.kml+xml; charset=utf-8' }, data: getKml() })
參考文章: http://www.jujens.eu/posts/en/2015/Jun/27/webdav-options/
來自: http://blog.kbiao.me/2016/05/21/CROS實現跨域時授權問題(401錯誤)的解決/