PHP解決跨域問題,你常用哪種方法?
什麼是跨域
跨域,指的是從一個域名去請求另外一個域名的資源。即跨域名請求!跨域時,瀏覽器不能執行其他域名網站的指令碼,是由瀏覽器的同源策略造成的,是瀏覽器施加的安全限制。
什麼是同源策略
同源策略是一種約定,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,瀏覽器很容易受到XSS、CSFR等攻擊。所謂同源是指"協議+域名+埠"三者相同,即便兩個不同的域名指向同一個ip地址,也非同源。
同源策略限制內容有:
- Cookie、LocalStorage、IndexedDB 等儲存性內容
- DOM 節點
- AJAX 請求傳送後,結果被瀏覽器攔截了
但是有三個標籤是允許跨域載入資源:
- <img src=XXX>
- <link href=XXX>
- <script src=XXX>
常見跨域場景有哪些?
當協議、子域名、主域名、埠號中任意一個不相同時,都算作不同域。不同域之間相互請求資源,就算作“跨域”。
常見跨域場景如下圖所示:
特別說明兩點:
第一:如果是協議和埠造成的跨域問題,那麼“前臺”是無能為力的。
第二:在跨域問題上,僅僅是通過“URL的首部”來識別,而不會根據域名對應的IP地址是否相同來判斷。“URL的首部”可以理解為“協議, 域名和埠必須匹配”。
這裡你或許有個疑問:請求跨域了,那麼請求到底發出去沒有?
跨域並不是請求發不出去,請求能發出去,服務端能收到請求並正常返回結果,只是結果被瀏覽器攔截了。
你可能會疑問明明通過表單的方式可以發起跨域請求,為什麼 Ajax 就不會?因為歸根結底,跨域是為了阻止使用者讀取到另一個域名下的內容,Ajax 可以獲取響應,瀏覽器認為這不安全,所以攔截了響應。
但是表單並不會獲取新的內容,所以可以發起跨域請求。同時也說明了跨域並不能完全阻止 CSRF,因為請求畢竟是發出去了。
PHP解決跨域問題的方法
跨域的嚴格一點來說就是隻要協議,域名,埠有任何一個的不同,就被當作是跨域。
比如,在實際專案中由於前後端分離當前端需要通過介面向後臺發起請求,此時就會出現跨域問題,那麼,這類問題需要如何解決呢?
其實php解決跨域問題很簡單,只需加上下面的程式碼就可以了:
header("Access-Control-Allow-Origin:*");
加上這行程式碼表示允許所有的域名訪問,不過為了安全起見,在實際專案中往往會限定只允許固定的幾個域名和方法發起的請求。
1、允許單個域名訪問
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'); //請求頭的限制
2、不限制域名
header('Access-Control-Allow-Origin:*');
header('Access-Control-Allow-Methods:POST');//表示只允許POST請求
header('Access-Control-Allow-Headers:x-requested-with, content-type');
3、允許多個域名訪問
在實際專案中最好指定能跨域訪問的域名,增加安全性。可以寫在一個公共類裡面,封裝一個方法呼叫。
// 設定能訪問的域名
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.html需要呼叫http://www.mano100.cn/server.php,我們可以這樣做,寫一個介面http://www.startphp.cn/server.php,由這個介面在後端去呼叫http://www.mano100.cn/server.php並拿到返回值,然後再返回給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...');
對PHP後端技術,對PHP架構技術感興趣的朋友,我的官方群1023755567點選此處,一起學習,相互討論。
群內已經有管理將知識體系整理好(原始碼,學習視訊等資料),歡迎加群免費領取。
部分資料截圖如下: