同源策略,它是由Netscape提出的一個著名的安全策略。如今全部支持JavaScript 的瀏覽器都會使用這個策略來對腳本和請求進行校驗,若不一樣源,則禁止使用。html
那若是判斷是否同源?主要根據三個維度,域名,協議,端口三個都相同纔算同源。
舉個🌰:前端
網站A | 網站B | 結果 |
---|---|---|
http://www.zhenai.com | http://i.zhenai.com | 不一樣源,域名不一樣 |
http://www.zhenai.com | http://www.zhenai.cn | 不一樣源,域名不一樣 |
http://www.zhenai.com | https://www.zhenai.com | 不一樣源,協議不一樣 |
http://www.zhenai.com | http://www.zhenai.com:3000 | 不一樣源,端口不一樣(默認端口80) |
這個主要是爲了防止惡意網站經過js獲取用戶其餘網站的cookie等用戶信息。vue
防止惡意網站經過iframe獲取頁面dom,從而竊取頁面的信息。node
防止惡意的請求攻擊服務器竊取數據信息。nginx
那是否是說非同源的請求就沒法實現呢?也不是,這就引出了咱們本文主要闡述的解決跨域請求問題的方法。ajax
jsonp能實現跨域是利用了img、script和link標籤自身的跨域能力。
咱們知道當img或者script中的src是一個連接的時候,瀏覽器會請求這個連接獲取資源,那麼這個連接若是是跨域的,瀏覽器也會請求,從而達到了跨域請求的一個功能。npm
var script = document.createElement('script'); script.src = 'http://localhost:3000/api/test.do?a=1&b=2&callback=cb'; $('body').append(script); function cb(res){ // do something console.log(res) }
能夠看到,咱們建立一個script標籤,將src改爲咱們要請求的接口,並將script添加在body中,那麼當瀏覽器解析到這個script時,會想src對應的服務器發送一個get請求,並將參數帶過去。
而後當瀏覽器接收到服務端返回的數據,就會觸發參數中callbak對應的回調函數cb,從而完成整個get請求。json
簡單粗暴segmentfault
①只支持get請求
②須要後臺配合,將返回結果包裝成callback(res)的形式後端
那若是黑客植入script腳本經過jsonp的方式對服務器進行攻擊,怎麼辦?
能夠經過頁面設置的內容安全協議csp
進行防範。
cors 是一個W3C標準,全稱是"跨域資源共享"(Cross-origin resource sharing),它容許瀏覽器向跨源服務器發送XMLHttpRequest請求,從而克服了 AJAX 只能同源使用的限制
cors 須要瀏覽器和服務器同時支持,整個 CORS通訊過程,都是瀏覽器自動完成不須要用戶參與,對於開發者來講,cors的代碼和正常的 ajax 沒有什麼差異,瀏覽器一旦發現跨域請求,就會添加一些附加的頭信息
可是,cors不支持ie10及如下版本。
瀏覽器將cors請求分爲簡單請求和複雜請求。
簡單請求則直接發送請求到服務器,只請求一次。
而複雜請求在正式請求前都會有預檢請求,在瀏覽器中都能看到有OPTIONS請求,用於向服務器請求權限信息的,須要請求兩次。
那如何區分是簡單請求仍是複雜請求呢?
簡單請求必需要同時知足下面三個條件:
類型 | 描述 |
---|---|
application/json | 消息主體是序列化後的 JSON 字符串 |
application/x-www-form-urlencoded | 數據被編碼爲鍵值對。這是標準的編碼格式 |
multipart/form-data | 須要在表單中進行文件上傳時,就須要使用該格式。常見的媒體格式是上傳文件之時使用的 |
text/plain | 數據以純文本形式(text/json/xml/html)進行編碼,其中不含任何控件或格式字符 |
application/json:
application/x-www-form-urlencoded:是Jquery的Ajax請求默認方式
不知足簡單請求的條件,那麼就是複雜請求。
複雜請求會在正式請求發送以前,先發一個預檢請求進行校驗,校驗經過後才能進行正式請求。
舉個🌰
瀏覽器如今要發送一個put的複雜請求,那麼在put請求發送以前,瀏覽器先發送一個options請求。
options請求頭信息:
OPTIONS /cors HTTP/1.1 Origin: localhost:3000 Access-Control-Request-Method: PUT // 表示使用的什麼HTTP請求方法 Access-Control-Request-Headers: X-Custom-Header // 表示瀏覽器發送的自定義字段 Host: localhost:3000 Accept-Language: zh-CN,zh;q=0.9 Connection: keep-alive User-Agent: Mozilla/5.0...
服務器收到options請求之後,檢查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段之後,確認容許跨源請求,就能夠作出迴應
options響應頭信息
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://localhost:3000 // 表示http://localhost:3000能夠訪問數據 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
當options請求經過以後發出正式的HTTP請求,假若options請求不經過,則服務器不容許這次訪問,從而拋出錯誤
options請求經過以後的,瀏覽器發出發請求
PUT /cors HTTP/1.1 Origin: http://api.zhenai.com Host: api.alice.com X-Custom-Header: value Accept-Language: en-US Connection: keep-alive User-Agent: Mozilla/5.0...
那這樣的話,若是頁面存在大量的複雜請求,豈不是每一個請求前面都要進行一次options的請求,那不會形成大量資源的浪費麼?
若是基於cors請求的方法來解決跨域問題,那麼複雜請求以前是須要進行一個options的請求的,但咱們能夠經過對options請求進行緩存來減輕請求的壓力。
在options請求中,咱們能夠經過設置響應頭的參數Access-Control-Max-Age
來對結果進行緩存
好比: Access-Control-Max-Age: 600
表示對options檢驗結果進行十分鐘的緩存
該字段的兼容性以下:
nginx解決跨域的問題跟以前的方法有所不一樣,它是經過服務器的方向代理,將前端訪問域名跟後端服務域名映射到同源的地址下,從而實現前端服務和後端服務的同源,那天然不存在跨域的問題了。
舉個🌰:
前端服務:http://localhost:3000
,
前端頁面路由:http://localhost:3000/page.html
,
後端服務:http://localhost:3001
,
後端接口路由:http://localhost:3001/api/test.do
能夠看出,兩個服務處於跨域的狀態
經過nginx的配置進行反向代理,便可實現先後端服務同源,以下:
server { listen 80; server_name localhost; location = / { proxy_pass http://localhost:3000; } location /api { proxy_pass http://localhost:3001; #指定容許跨域的方法,*表明全部 add_header Access-Control-Allow-Methods *; #預檢命令的緩存,若是不緩存每次會發送兩次請求 add_header Access-Control-Max-Age 3600; #帶cookie請求須要加上這個字段,並設置爲true add_header Access-Control-Allow-Credentials true; #表示容許這個域跨域調用(客戶端發送請求的域名和端口) #$http_origin動態獲取請求客戶端請求的域 不用*的緣由是帶cookie的請求不支持*號 add_header Access-Control-Allow-Origin $http_origin; #表示請求頭的字段 動態獲取 add_header Access-Control-Allow-Headers $http_access_control_request_headers; #OPTIONS預檢命令,預檢命令經過時才發送請求 #檢查請求的類型是否是預檢命令 if ($request_method = OPTIONS){ return 200; } } }
其實nginx不只僅只是用於解決跨域問題,而是涉及到不少服務器資源分配的處理,在此就不詳細探討了。
其實,在咱們主流使用的MVVM框架中,配置項裏面也提供解決跨域問題的能力,繼續舉個🌰,以vue2.x爲例,咱們能夠經過在config/index.js
中添加配置項實現跨域請求:
proxyTable: { '/apis': { // 測試環境 target: 'http://www.zhenai.com/', // 接口域名 changeOrigin: true, //是否跨域 pathRewrite: { '^/apis': '' //須要rewrite重寫的, } } }
其實原理很簡單,就是在咱們使用npm run dev
命中,啓動了一個node服務,而後將前端發出的請求發送到node服務,再將該服務轉發到本來的後臺服務,在這過程當中實現了一層代理,由一個node服務發送一個請求到另一個後臺服務,天然也沒有了瀏覽器所限制的跨域問題。
https://blog.csdn.net/yingwang9/article/details/90716623
https://www.jianshu.com/p/d89c62572acd
https://segmentfault.com/a/1190000019227927?utm_source=tag-newest