什麼是跨域訪問? 因爲瀏覽器同源策略,凡是發送請求url的協議、域名、端口三者之間任意一個與當前頁面地址不一樣即爲跨域。存在跨域的狀況:
網絡協議不一樣,如http協議訪問https協議。javascript
端口不一樣,如80端口訪問8080端口。php
域名不一樣,如qianduanblog.com訪問baidu.com。前端
子域名不一樣,如abc.qianduanblog.com訪問def.qianduanblog.com。java
域名和域名對應ip,如www.a.com訪問20.205.28.90.nginx
請求的數據是XHR(XMLHttpRequest)瀏覽器會有跨域的限制。若是不是XHR類型的則不會限制 ajax
咱們的瀏覽器須要禁止跨域呢?
這裏舉兩個常見的例子:
1.咱們知道一幫瀏覽器都使用cookie去儲存咱們的用戶登陸信息。若是容許跨域訪問,那麼別的網站只須要一段腳本就能夠獲取你的cookie,從而冒充你的身份去登陸網站,形成很是大的安全問題。
2.假設如今有兩個不一樣域,若是沒有這一安全策略,那麼當用戶在訪問a.com時,a.com的一段腳本就能夠在不加載b.com的頁面而隨意修改或者獲取b.com上面的內容。這樣將會致使b.com頁面的頁面發生混亂。apache
跨域的解決辦法 其實跨域的解決辦法從本質上去區分,能夠劃分爲兩類。
第一類:被調用方
第二類:調用方
接下來咱們就開始基於這兩種類型去解決跨域問題。json
被調用方的解決辦法 這裏在說明代碼以前,咱們要先知道在咱們的請求中能夠分爲兩種請求:(1)簡單請求(2)非簡單請求api
簡單請求: 在heard裏面無定義頭 Content-Type爲一下幾種: text/plain multipart/from-data application/x-www-form-urlencoded 常見非簡單請求: put,delete方法的ajax請求 發送json格式的ajax請求 帶自定義頭的ajax請求
簡單請求與非簡單請求的區別 簡單請求:瀏覽器會先自行後判斷 非簡單請求:瀏覽器先判斷後執行(OPTIONS)
所以咱們在訪問非簡單請求的時候,咱們能夠看到發送的方法會是opttions。意思就是服務器先詢問服務是否存在相關資源。當存在相關資源的時候才能正常返回。
接下來咱們就能夠開展被調用方的解決方案:
解決方案1:JSONP 什麼是JSONP?
Jsonp(JSON with Padding) 是 json 的一種"使用模式",可讓網頁從別的域名(網站)那獲取資料,即跨域讀取數據。
下面咱們展現JSONP的使用案例跨域
#這裏咱們模仿ajax進行一個跨越訪問,其datatype須要設置爲jsonp #裏面的jsonp,設置回調函數的名稱爲callback <script type="text/javascript"> $.ajax({ url:"http://crossdomain.com/services.php", dataType:'jsonp', data:'', jsonp:'callback', success:function(result) { for(var i in result) { alert(i+":"+result[i]);//循環輸出a:1,b:2,etc. } }, timeout:3000 }); </script>
#這是後臺返回數據的方法 #能夠看出其實就是把返回的數據再包含在與前端的回調函數的名字同樣的函數體便可 function api_jsonp_encode($json) { if (!empty($_GET['callbak'])) { return $_GET['callbak'] . '(' . $json . ')'; // jsonp } return $json; // json }
jsonp其實做爲一種跨域的解決手段其實存在比較明顯的缺陷:
1.服務器須要改動代碼 2.只支持get方法 3.發送的不是XHR
解決方法2:增長header頭
設置頭文件: //設置請求域名 header('Access-Control-Allow-Origin:*'); //設置請求頭 header("Access-Control-Allow-Headers:Origin, X-Requested-With, Content-Type, Accept, Authorization"); //設置請求方法 header('Access-Control-Allow-Methods:GET, POST, PUT,DELETE,OPTIONS,PATCH'); header('Access-Control-Allow-Methods:*'); //設置預檢命令的緩存頭 header('Access-Control-Max-Age:3600'); #這裏說明一下,*號表明沒有限制全匹配。所以你能夠看到們設置的請求域名,還有請求方法都是有*號配置。 #設置御檢命令緩存頭就是讓瀏覽器當發送一次御檢命令後,後期就能夠在緩存時間內直接使用緩存,而不須要再次請求。其第二個參數爲緩存時間
可是咱們直接上述的配置,只能知足通常狀況。由於若是當跨域訪問的時候攜帶cookie,或者自定義頭的時候咱們仍是不能成功跨域的所以。針對這兩種狀況,咱們能夠作出如下的調整修改
帶Cookie的跨域 //設置容許攜帶Cookie header("Access-Control-Allow-Credentials: true"); //須要設置與服務器匹配的域名,不能使用* header('Access-Control-Allow-Origin:http://www.xxx.com'); #若是可能須要匹配多個容許域名,能夠參考下面的動圖作法 //經過系統變量獲取origin $origin = isset($_SERVER['HTTP_ORIGIN'])? $_SERVER['HTTP_ORIGIN'] : ''; //設置容許數組列表 $allow_origin = array( 'http://client1.runoob.com', 'http://client2.runoob.com' ); //判斷是否存在於數組中 if(in_array($origin, $allow_origin)){ header('Access-Control-Allow-Origin:'.$origin); }
自定義請求頭的跨域處理 通常狀況下可使用如下的代碼設置請求頭 header("Access-Control-Allow-Headers:Origin, X-Requested-With, Content-Type, Accept, Authorization"); 可是若是不生效。能夠把對應的請求頭也寫進去例如: header("Access-Control-Allow-Headers:Origin, X-Requested-With, Content-Type, Accept, Authorization,test-header");
以上就是經過被調用方設置header頭去解決對應的跨域問題。
可是上述的方法仍是存在必定的侷限性。由於加header頭本質上仍是會改動了咱們的代碼。那麼咱們還有沒有更好的方式呢?其實咱們能夠基於服務器的改造
解決方法3:配置服務器的
ngix的配置 server { listen 80; #監聽80端口 server_name demo.com; #本地域名 location / { proxy_pass http://XXXX.com; #把全部請求都轉發到此須要提供服務的端口下 add_header Access-Control-Allow-Headers $http_access_control_request_headers'; add_header Access-Control-Allow-Origin $http_origin; add_header Access-Control-Allow-Methods *; add_header Access-Control-Max-Age 3600; add_header Access-Control-Allow-Credentials true; #判斷若是進入的是與預檢查 if ($request_method = OPTIONS){ return 200; } } }
.apache配置 //修改vhost文件 <virtualhost *:80=""> DocumentRoot "C:/htdocs/demo" #目錄路徑 ServerName demo.com #域名 ##ErrorLog "logs/dummy-host.localhost-error.log" ##CustomLog "logs/dummy-host.localhost-access.log" common #設置轉發(須要開啓 proxy_module,proxy_http) ProxyPass/ http://XXXX.com #把請求頭的Access-Controller-Request-Headerss值返回到Access-Control-Allow-Headers Header always set Access-Control-Allow-Headers "expr=%{req:Access-Controller-Request-Headers}" #把請求頭的orgin值返回到Access-Control-Allow-Orgin Header always set Access-Control-Allow-Orgin "expr=%{req:orgin}" Header always set Access-Control-Allow-Methods "*" Header always set Access-Control-Allow-Credentials "true" Header always set Access-Control-Max-Age "3600" #處理預檢命令OPTIONS,直接返回204(開啓 headers_module,rewrite_module) RewriteEngine On RewriteCond%{REQUEST_METHOD}OPTIONS RewriteRule^(.*)$"/"[R=204,L] </virtualhost>
以上的服務器配置就是用於跨域請求的被調用方的配置
#咱們剛剛討論完了被調用方的設置。那麼咱們再來講一下調用方的設置。由於在咱們實際的開發中,有時候被調用方有多是不配合咱們進行跨域訪問的修改的,那麼咱們只能本身解決跨域問題了。 在通常開發中咱們解決的思路就是使用反向代理。而配置反向代理只能經過咱們的服務器去訪問對方的服務器,從而繞過瀏覽器的這一個檻。
nginx: server { listen 80; #監聽80端口 server_name demo.com; #本地域名 #後期全部的調用接口都須要使用該地址 #例如:咱們訪問http://aa.com/index/login #改成:/ajaxserver/index/login location/ajaxserver { proxy_pass http://XXXX.com; #須要跨域訪問接口的的地址 } }
<virtualhost *:80=""> DocumentRoot "C:/htdocs/demo" #目錄路徑 ServerName demo.com #域名 ##ErrorLog "logs/dummy-host.localhost-error.log" ##CustomLog "logs/dummy-host.localhost-access.log" common #後期全部的調用接口都須要使用該地址 #例如:咱們訪問http://aa.com/index/login #反向代理設置 ProxyPass /ajaxserver http://XXXX.com </virtualhost>
以上就是咱們在跨域工做中趕上的常見狀況以及解決思路和事例代碼。
轉載於猿2048:➩《分享跨域訪問的解決方案與基礎分析》