跨域,指的是從一個域名去請求另一個域名的資源。即跨域名請求!跨域時,瀏覽器不能執行其餘域名網站的腳本,是由瀏覽器的同源策略形成的,是瀏覽器施加的安全限制。php
同源策略是一種約定,它是瀏覽器最核心也最基本的安全功能,若是缺乏了同源策略,瀏覽器很容易受到XSS、CSFR等攻擊。所謂同源是指"協議+域名+端口"三者相同,即使兩個不一樣的域名指向同一個ip地址,也非同源。html
同源策略限制內容有:前端
可是有三個標籤是容許跨域加載資源:mysql
當協議、子域名、主域名、端口號中任意一個不相同時,都算做不一樣域。不一樣域之間相互請求資源,就算做「跨域」。webpack
特別說明兩點: web
第一:若是是協議和端口形成的跨域問題,那麼「前臺」是無能爲力的。sql
第二:在跨域問題上,僅僅是經過「URL的首部」來識別,而不會根據域名對應的IP地址是否相同來判斷。「URL的首部」能夠理解爲「協議, 域名和端口必須匹配」。json
這裏你或許有個疑問:請求跨域了,那麼請求到底發出去沒有?後端
跨域並非請求發不出去,請求能發出去,服務端能收到請求並正常返回結果,只是結果被瀏覽器攔截了。
你可能會疑問明明經過表單的方式能夠發起跨域請求,爲何 Ajax 就不會?由於歸根結底,跨域是爲了阻止用戶讀取到另外一個域名下的內容,Ajax 能夠獲取響應,瀏覽器認爲這不安全,因此攔截了響應。
可是表單並不會獲取新的內容,因此能夠發起跨域請求。同時也說明了跨域並不能徹底阻止 CSRF,由於請求畢竟是發出去了。
PHP解決跨域問題的方法
跨域的嚴格一點來講就是隻要協議,域名,端口有任何一個的不一樣,就被看成是跨域。
好比,在實際項目中因爲先後端分離當前端須要經過接口向後臺發起請求,此時就會出現跨域問題,那麼,這類問題須要如何解決呢?
其實php解決跨域問題很簡單,只需加上下面的代碼就能夠了:
header("Access-Control-Allow-Origin:*");
加上這行代碼表示容許全部的域名訪問,不過爲了安全起見,在實際項目中每每會限定只容許固定的幾個域名和方法發起的請求。
一、容許單個域名訪問
header('Access-Control-Allow-Origin:http://www.startphp.cn'); header('Access-Control-Allow-Methods:POST'); //表示只容許POST請求 header('Access-Control-Allow-Headers:x-requested-with, content-type'); //請求頭的限制
二、不限制域名
header('Access-Control-Allow-Origin:*'); header('Access-Control-Allow-Methods:POST');//表示只容許POST請求 header('Access-Control-Allow-Headers:x-requested-with, content-type');
三、容許多個域名訪問
在實際項目中最好指定能跨域訪問的域名,增長安全性。能夠寫在一個公共類裏面,封裝一個方法調用。
// 設置能訪問的域名 static public $originarr = [ 'https://test1.com', 'https://test2.com', ]; /** * 公共方法調用 */ static public function setheader() { // 獲取當前跨域域名 $origin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : ''; if (in_array($origin, self::$originarr)) { // 容許 $originarr 數組內的 域名跨域訪問 header('Access-Control-Allow-Origin:' . $origin); // 響應類型 header('Access-Control-Allow-Methods:POST,GET'); // 帶 cookie 的跨域訪問 header('Access-Control-Allow-Credentials: true'); // 響應頭設置 header('Access-Control-Allow-Headers:x-requested-with,Content-Type,X-CSRF-Token'); } }
在php上如何實現
<?php // 制定容許其餘域名訪問 header("Access-Control-Allow-Origin:*"); // 響應類型 header('Access-Control-Allow-Methods:POST'); // 響應頭設置 header('Access-Control-Allow-Headers:x-requested-with, content-type'); //$callback = isset($_REQUEST['callback']) ? trim($_REQUEST['callback']) : ''; //jsonp回調參數,必需 function getKey($key,$default=""){ return trim(isset($_REQUEST[$key])?$_REQUEST[$key]:$default); } $id = getKey("id"); $conn = mysqli_connect("localhost","root","","test") or die("鏈接失敗"); $conn->query("set names utf8"); $sql = "select * from data where ".$id." is not null"; $result = $conn->query($sql); $arr = []; while($row=$result->fetch_assoc()){ array_push($arr,json_encode($row)); } $json = json_encode($arr); //json 數據 print_r($json);
4 代理,這種常常用
好比http://www.startphp.cn/index....://www.mano100.cn/server.php,咱們能夠這樣作,寫一個接口http://www.startphp.cn/server...,由這個接口在後端去調用http://www.mano100.cn/server....,而後再返回給index.html,這就是一個代理的模式。至關於繞過了瀏覽器端,天然就不存在跨域問題。
5 Nginx反向代理
使用nginx反向代理實現跨域,是最簡單的跨域方式。只須要修改nginx的配置便可解決跨域問題,支持全部瀏覽器,支持session,不須要修改任何代碼,而且不會影響服務器性能。
實現思路:經過nginx配置一個代理服務器(域名與domain1相同,端口不一樣)作跳板機,反向代理訪問domain2接口,而且能夠順便修改cookie中domain信息,方便當前域cookie寫入,實現跨域登陸。
修改配置文件nginx.conf,以下:
// proxy服務器 server { listen 81; server_name www.domain1.com; location / { proxy_pass http://www.domain2.com:8080; #反向代理 proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie裏域名 index index.html index.htm; # 當用webpack-dev-server等中間件代理接口訪問nignx時,此時無瀏覽器參與,故沒有同源限制,下面的跨域配置可不啓用 add_header Access-Control-Allow-Origin http://www.domain1.com; #當前端只跨域不帶cookie時,可爲* add_header Access-Control-Allow-Credentials true; } }
配置修改好後,再重啓nginx。
index.html文件訪問代理服務器
// index.html var xhr = new XMLHttpRequest(); // 前端開關:瀏覽器是否讀寫cookie xhr.withCredentials = true; // 訪問nginx中的代理服務器 xhr.open('get', 'http://www.domain1.com:81/?user=admin', true); xhr.send();
server.js
// server.js var http = require('http'); var server = http.createServer(); var qs = require('querystring'); server.on('request', function(req, res) { var params = qs.parse(req.url.substring(2)); // 向前臺寫cookie res.writeHead(200, { 'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly' // HttpOnly:腳本沒法讀取 }); res.write(JSON.stringify(params)); res.end(); }); server.listen('8080'); console.log('Server is running at port 8080...');