javascript
jsonp-反向代理-CORS解决JS跨域问题的个人总结
jsonp-反向代理-CORS解決JS跨域問題的個人總結
網上說了很多很多,但是看完之后還是很混亂,所以我自己重新總結一下。解決 js 跨域問題一共有8種方法,
各個方法都有各自的優缺點,但是目前前端開發方面比較常用的是 jsonp,反向代理,CORS:
-
CORS是跨源資源分享(Cross-Origin Resource Sharing)的縮寫。它是W3C標準,是跨源AJAX請求的根本解決方法。優點是正統,符合標準,缺點是:
- 但是需要服務器端配合,比較麻煩。
-
JSONP 優點是對舊式瀏覽器支持較好,缺點是:
- 但是只支持 get 請求。
- 有安全問題(請求代碼中可能存在安全隱患)。
- 要確定jsonp請求是否失敗并不容易
-
反向代理都能夠兼容以上的確定,但是僅僅作為前端開發模式的時候使用,在正式上線環境較少用到。
- 因為開發環境的域名跟線上環境不一樣才需要這樣處理。
- 如果線上環境太復雜,本身也是多域(后面說到的同源策略問題,多子域,或者多端口問題),那么需要采用 jsonp 或者 CORS 來處理。
一、什么是跨域問題
跨域問題一般只出現在前端開發中使用 javascript 進行網絡請求的時候,瀏覽器為了安全訪問網絡請求的數據而進行的限制。
提示的錯誤大致如下:
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://XXXXXX' is therefore not allowed access.
二、為什么會出現跨域問題
因為瀏覽器收到同源策略的限制,當前域名的js只能讀取同域下的窗口屬性。
2.1 同源策略
同源指的是三個源頭同時相同:
- 協議相同
- 域名相同
- 端口相同
舉例來說,http://www.example.com/dir/page.html這個網址,
協議是 http:// 域名是 www.example.com 端口是80 //它的同源情況如下: http://www.example.com/dir2/other.html:同源 http://example.com/dir/other.html:不同源(域名不同) http://v2.www.example.com/dir/other.html:不同源(域名不同) http://www.example.com:81/dir/other.html:不同源(端口不同)同源策略限制了以下行為:
- Cookie、LocalStorage 和 IndexDB 無法讀取
- DOM 和 JS 對象無法獲取
- Ajax請求發送不出去
詳細的同源策略相關,可以參考http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html
三、解決跨域問題
3.1 反向代理方式
反向代理和正向代理的區別:
- 正向代理(Forward Proxy),通常都被簡稱為代理,就是在用戶無法正常訪問外部資源,比方說受到GFW的影響無法訪問twitter的時候,我們可以通過代理的方式,讓用戶繞過防火墻,從而連接到目標網絡或者服務。
- 反向代理(Reverse Proxy)是指以代理服務器來接受 Internet 上的連接請求,然后將請求轉發給內部網絡上的服務器,并將從服務器上得到的結果返回給 Internet 請求連接的客戶端,此時,代理服務器對外就表現為一個服務器。
那么我們可以理解為反向代理
如何使用反向代理服務器來達到跨域問題解決:
- 前端ajax請求的是本地反向代理服務器
-
本地反向代理服務器接收到后:
- 修改請求的 http-header 信息,例如 referer,host,端口等
- 修改后將請求發送到實際的服務器
- 實際的服務器會以為是同源(參考同源策略)的請求而作出處理
現在前端開發一般使用 nodejs來做本地反向代理服務器
// 在 express 之后引入路由 var app = express();var apiRoutes = express.Router();app.use(bodyParser.urlencoded({extended:false}))// 自定義 api 路由 apiRoutes.get("/lyric", function (req, res) {var url = "https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg";axios.get(url, {headers: { // 修改 headerreferer: "https://c.y.qq.com/",host: "c.y.qq.com"},params: req.query}).then((response) => {var ret = response.dataif (typeof ret === "string") {var reg = /^\w+\(({[^()]+})\)$/;var matches = ret.match(reg);if (matches) {ret = JSON.parse(matches[1])}}res.json(ret)}).catch((e) => {console.log(e)}) });// 使用這個路由 app.use("/api", apiRoutes);3.2 JSONP 方式
JSONP有些文章會叫動態創建script,因為他確實是動態寫入 script 標簽的內容從而達到跨域的效果:
- AJAX 無法跨域是受到“同源政策”的限制,但是帶有src屬性的標簽(例如<script>、<img>、<iframe>)是不受該政策限制的,因此我們可以通過向頁面中動態添加<script>標簽來完成對跨域資源的訪問,這也是 JSONP 方案最核心的原理,換句話理解,就是利用了【前端請求靜態資源的時候不存在跨域問題】這個思路。
- JSONP(JSON with Padding)是數據格式JSON的一種“使用模式”。
- JSONP 只能用 get 方式。
實現 jsonp 的方式:
引用來自https://segmentfault.com/a/1190000012469713的圖
- 客戶端和服務器端約定一個參數名是代表 jsonp 請求的,例如約定 callback 這個參數名。
- 然后服務器端準備好針對之前約定的 callback 參數請求的 javascript 文件,這個文件里面要有一個函數名,要跟客戶端請求的時候的函數名要保持一致。(如下面例子:ip.js)
- 然后客戶端注冊一個本地運行的函數,并且函數的名字要跟去請求服務器進行 callback 回調的函數的名字要一致。(如下面例子:foo 函數跟請求時候callback=foo的名字是一致的)
- 然后客戶端對服務器端進行 jsonp 的方式請求。
- 服務器端返回剛才配置好的js 文件(ip.js)到客戶端
-
客戶端瀏覽器,解析script標簽,并執行返回的javascript文件,此時數據作為參數,傳入到了客戶端預先定義好的 callback 函數里。
- 相當于本地執行注冊好foo 函數,然后獲取了一個foo 函數,并且這個獲取的 foo 函數里面包含了傳入的參數(例如 foo({XXXXX}))
這是一個實例 demo:
服務器端文件ip.js
foo({"ip": "8.8.8.8" });客戶端文件 jsonp.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><title></title><script>// 動態插入 script 標簽到 html 中function addScriptTag(src) {var script = document.createElement('script');script.setAttribute("type","text/javascript");script.src = src;document.body.appendChild(script);}// 獲取 jsonp 文件window.onload = function () {addScriptTag('http://example.com/ip?callback=foo');}// 執行本地的 js 邏輯,這個要跟獲取到的 jsonp 文件的函數要一致function foo(data) {console.log('Your public IP address is: ' + data.ip);};</script> </head> <body> </body> </html>3.3 CORS 方式
CORS是一個W3C標準,全稱是"跨域資源共享"(Cross-origin resource sharing)。它允許瀏覽器向跨源服務器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。
- CORS需要瀏覽器和服務器同時支持。目前,所有瀏覽器都支持該功能,IE瀏覽器不能低于IE10。
- 整個CORS通信過程,都是瀏覽器自動完成,不需要用戶參與。對于開發者來說,CORS通信與同源的AJAX通信沒有差別,代碼完全一樣。瀏覽器一旦發現AJAX請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感覺。
3.3.1 CORS的請求分為兩類:
- 簡單請求
- 非簡單請求
只要同時滿足以下兩大條件,就屬于簡單請求。
(1) 請求方法是以下三種方法之一:
- HEAD
- GET
- POST
(2)HTTP的頭信息不超出以下幾種字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三個值application/x-www-form-urlencoded、multipart/form-data、text/plain
凡是不同時滿足上面兩個條件,就屬于非簡單請求。
3.3.2 簡單請求
如果是簡單請求的話,會自動在頭信息之中,添加一個Origin字段
GET /cors HTTP/1.1 Origin: http://api.bob.com Host: api.alice.com Accept-Language: en-US Connection: keep-alive User-Agent: Mozilla/5.0...這個Origin對應服務器端的Access-Control-Allow-Origin設置,所以一般來說需要在服務器端加上這個Access-Control-Allow-Origin 指定域名|*
3.3.3 非簡單請求
如果是非簡單請求的話,會在正式通信之前,增加一次HTTP查詢請求,稱為"預檢"請求(preflight)。
瀏覽器先詢問服務器,當前網頁所在的域名是否在服務器的許可名單之中,以及可以使用哪些HTTP動詞和頭信息字段。只有得到肯定答復,瀏覽器才會發出正式的XMLHttpRequest請求,否則就報錯。
需要注意這里是會發送2次請求,第一次是預檢請求,第二次才是真正的請求!首先發出預檢請求:
// 預檢請求 OPTIONS /cors HTTP/1.1 Origin: http://api.bob.com Access-Control-Request-Method: PUT Access-Control-Request-Headers: X-Custom-Header Host: api.alice.com Accept-Language: en-US Connection: keep-alive User-Agent: Mozilla/5.0..除了Origin字段,"預檢"請求的頭信息包括兩個特殊字段。
(1)Access-Control-Request-Method
該字段是必須的,用來列出瀏覽器的CORS請求會用到哪些HTTP方法,上例是PUT。
(2)Access-Control-Request-Headers
該字段是一個逗號分隔的字符串,指定瀏覽器CORS請求會額外發送的頭信息字段,上例是X-Custom-Header。
然后服務器收到"預檢"請求以后:
檢查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,確認允許跨源請求,就可以做出回應。
// 預檢請求的回應 HTTP/1.1 200 OK Date: Mon, 01 Dec 2008 01:15:39 GMT Server: Apache/2.0.61 (Unix) Access-Control-Allow-Origin: http://api.bob.com Access-Control-Allow-Methods: GET, POST, PUT Access-Control-Allow-Headers: X-Custom-Header Content-Type: text/html; charset=utf-8 Content-Encoding: gzip Content-Length: 0 Keep-Alive: timeout=2, max=100 Connection: Keep-Alive Content-Type: text/plain最后一旦服務器通過了"預檢"請求:
以后每次瀏覽器正常的CORS請求,就都跟簡單請求一樣,會有一個Origin頭信息字段。服務器的回應,也都會有一個Access-Control-Allow-Origin頭信息字段。
// 以后的請求,就像拿到了通行證之后,就不需要再做預檢請求了。 PUT /cors HTTP/1.1 Origin: http://api.bob.com Host: api.alice.com X-Custom-Header: value Accept-Language: en-US Connection: keep-alive User-Agent: Mozilla/5.0...詳情參考這里http://www.ruanyifeng.com/blog/2016/04/cors.html
參考文檔:
- 前端解決跨域問題的8種方案
- 瀏覽器同源政策及其規避方法
- https://tonghuashuo.github.io/blog/jsonp.html
- http://www.cnblogs.com/yuzhongwusan/archive/2012/12/11/2812849.html
- http://www.cnblogs.com/dowinning/archive/2012/04/19/json-jsonp-jquery.html
- https://segmentfault.com/a/1190000002438126
總結
以上是生活随笔為你收集整理的jsonp-反向代理-CORS解决JS跨域问题的个人总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: leetcode 66 Plus One
- 下一篇: javascript杂记