為 RESTful API 配置 CORS 實現跨域請求


利用 Ruby on Rails 可以很方便地實現 RESTful API,但如果我們需要通過 AJAX 跨域調用的話,怎么辦?

說到 AJAX 跨域,很多人最先想到的是 JSONP。的確,JSONP 我們已經十分熟悉,也使用了多年,從本質上講,JSONP 的原理是給頁面注入一個 <script>,把遠程 JavaScript 放在頁面上執行。這種做法會帶來一個顯而易見的問題:如果調用的來源被攻擊或篡改,那什么東西都可以注入到頁面里,造成 XSS 漏洞。另外,JSONP 本質上已經不是 XMLHttpRequest,所以在錯誤處理上也沒有什么選擇。而且 JSONP 只支持 GET 請求,所以 RESTful API 就沒辦法了。

這也就是為什么我們需要 CORS。CORS 是 Cross Origin Resource Sharing 的縮寫,定義了瀏覽器和服務器間共享內容的新方式,通過它瀏覽器和服務器可以安全地進行跨域訪問,它是 JSONP 的現代繼任者。服務器上的 CORS 配置可以精細地指定允許跨域訪問的條件:來源域、HTTP 方法、請求頭、內容類型……等等。並且,CORS 讓 XMLHttpRequest 也可以跨域,我們可以像往常一樣編寫 AJAX 調用代碼。

所有現代瀏覽器都支持 CORS,所以你應該可以放心地使用它,只有在需要兼容老舊瀏覽器的場合,才用 JSONP 做 fallback。

支持 CORS 的瀏覽器在嘗試進行跨域 XMLHttpRequest 時,會先發出一個“事前檢查”,就是一個 OPTIONS請求,其中會包括一些有用的請求頭:

Access-Controll-Request-Headers: accept, content-type
Access-Controll-Request-Method: POST

接着服務器會做出響應:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-Requested-With, Content-Type, Accept
Access-Control-Max-Age: 1728000

最后瀏覽器會根據服務器的響應頭,判斷請求是否在服務器規定的范圍內。比如來源是否在 Access-Control-Allow-Origin 里,HTTP 方法是不是在 Access-Control-Allow-Methods 里面,有沒有不在 Allow-Headers 里面的請求頭。如果以上條件都符合,那么瀏覽器就會放行這次請求,並且在 Access-Control-Max-Age 指定的時間內(單位是秒,以上設置的是 20 天)不需要再進行這種“事前檢查”。

在以上服務器響應頭中,Access-Control-Allow-Headers 不可以使用通配符。所以如果你要允許所有請求頭,不妨把瀏覽器發來的 Access-Control-Request-Headers 直接返回。

事實上,如果跨域請求是“簡單請求”,也就是 HTTP 方法為 GETHEADPOST,請求體的 MIME Type 是以下其中一種:application/x-www-form-urlencodedmultipart/form-data 或者 text/plain,並且沒有自定義的請求頭。這時瀏覽器只根據請求頭中的 Origin 和服務器返回的 Access-Control-Allow-Origin 就可以判斷了。但我們是 RESTful API,請求體是 application/json,所以只能用上面那種“事前檢查”的方式。

另外,利用 CORS 還可以在跨域請求中發送 Cookie,這個特性是很有用的。只需要為 XMLHttpRequest 對象設置 withCredentials 屬性:

var xhr = new XMLHttpRequest(); xhr.withCredentials = true; 

但這種情況下,就不能指定 Access-Control-Allow-Origin: *,而是必須指定一個來源,比如 http://mydomain.com

下面來說說在服務器端怎么配置,以 Rails 框架為例。注意:這只是一個很簡單的例子,為了展示其原理,建議只在安全要求不高的項目上使用這種方法。Rails 有專門的 Rack CORS 中間件可以處理這個問題。

在一個 Controller(或者 application_controller.rb )中添加 before_filter 和 after_filter。前者用來回應瀏覽器的“事前檢查”,如果瀏覽器發來了 OPTIONS 請求,則返回一些響應頭,並結束處理;后者則用來給響應添加 CORS 的響應頭:

# some_controller.rb before_filter :cors_preflight_check after_filter :cors_set_headers # ... def cors_preflight_check if request.method == 'OPTIONS' headers['Access-Controll-Allow-Origin'] = '*' headers['Access-Controll-Allow-Methods'] = 'POST, GET, OPTIONS' headers['Access-Controll-Allow-Headers'] = 'X-Requested-With, Content-Type, Accept' headers['Access-Controll-Max-Age'] = '1728000' render :text => '', :content-type => 'text/plain' end end def cors_set_headers headers['Access-Controll-Allow-Origin'] = '*' headers['Access-Controll-Allow-Methods'] = 'POST, GET, OPTIONS' headers['Access-Controll-Max-Age'] = '1728000' end 

最后,別忘了在 routes.rb 中允許 OPTIONS 請求:

match 'controller', to: 'controller#action', via: [:options] 添加此行 resources :controller

轉載自:https://blog.csdn.net/z69183787/article/details/53102888


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM