什么是跨域
不同域名之間的資源訪問(wèn)
解決方法
JSONP,CROS
JSONP
原理:html帶src屬性的標(biāo)簽都可以跨域引用,jsonp通過(guò)寫入和讀取src文件內(nèi)容來(lái)實(shí)現(xiàn)跨域。
用法
前端:
$.ajax({
type: 'GET',
url: captchaUrl,
dataType:'jsonp',
jsonp:'jsoncallback',
success: function(res){
$('#captcha').attr('src', res.url);
}
});
后端:
$jsoncallback = Yii::$app->request->get('jsoncallback', '');
if ($jsoncallback) {
Yii::$app->response->format = Response::FORMAT_JSONP;
$result = ['callback' => $jsoncallback, 'data' => $result];
}
- 優(yōu)點(diǎn):
支持較舊的瀏覽器
可以向不支持CORS的網(wǎng)站請(qǐng)求數(shù)據(jù)
- 缺點(diǎn):
只支持get請(qǐng)求,導(dǎo)致傳輸文本長(zhǎng)度受限
CROS
原理:請(qǐng)求header添加Access-Control-Allow-Origin相關(guān)屬性
用法:
前端
$.ajax({
type:"POST",
url: url,
data: data,
xhrFields: {
withCredentials: true //需要寫入cookie時(shí)設(shè)置為true,后端也要相應(yīng)設(shè)置
},
crossDomain: true, //允許跨域
success:function(res){
success(res);
},
error: function(res){
error(res);
}
})
后端
設(shè)置header里的cros屬性,下面是yii2的方法
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors["cors"] = [
'class' => Cors::className(),
'cors' => [
'Origin' => ['http://www.xxxxxx.com', ' //允許的域名,設(shè)置為'*'表示全部允許
'Access-Control-Request-Method' => ['POST', 'GET'], //允許的請(qǐng)求方式
'Access-Control-Allow-Credentials' => true, //需要寫入cookie的時(shí)候要設(shè)置為true,前端也要相應(yīng)設(shè)置
],
];
return $behaviors;
}
寫入cookie時(shí),如果是簡(jiǎn)單請(qǐng)求,可能有第一次請(qǐng)求無(wú)法寫入,第二次請(qǐng)求才能寫入的問(wèn)題,需要進(jìn)一步實(shí)驗(yàn)。
深入原理請(qǐng)參考:
http://www.ruanyifeng.com/blog/2016/04/cors.html
圖片的跨域問(wèn)題
首先圖片受到url長(zhǎng)度限制不能使用get請(qǐng)求傳輸,所以不能使用jsonp方式跨域。
如果遇到不支持cros的瀏覽器要跨域圖片可以使用flash方式跨域,webuploader,ueditor等都支持配置使用swf文件進(jìn)行圖片傳輸。
服務(wù)器web目錄要添加一個(gè)跨域文件crossdomain.xml,內(nèi)容如下:
<?xml version="1.0" encoding="UTF-8"?>
<cross-domain-policy>
<allow-access-from domain="www.xxxx.com" />
</cross-domain-policy>
簡(jiǎn)單請(qǐng)求和復(fù)雜請(qǐng)求
簡(jiǎn)單請(qǐng)求response直接添加跨域信息
Access-Control-Allow-Origin: http://api.xxx.com
Access-Control-Allow-Credentials: true(可選)
Access-Control-Expose-Headers: FooBar(可選)
復(fù)雜請(qǐng)求會(huì)有一次OPTIONS預(yù)請(qǐng)求,在預(yù)請(qǐng)求中返回:
Access-Control-Allow-Origin: http://api.xxx.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400 (緩存時(shí)間單位秒,在時(shí)間內(nèi)再次訪問(wèn)該接口就不會(huì)有options預(yù)請(qǐng)求了)
然后再進(jìn)行正式請(qǐng)求,返回內(nèi)容和簡(jiǎn)單請(qǐng)求一樣
laravel框架cros跨域?qū)崿F(xiàn):
laravel路由區(qū)分請(qǐng)求方式,options預(yù)請(qǐng)求要單獨(dú)處理。目前自己寫了一個(gè)中間件實(shí)現(xiàn)跨域。
<?php
namespace App\Http\Middleware;
use Closure;
class Cors
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$response = $next($request);
$allowOrigins = [
'http://localhost:8080',
'http://www.xxxxx.cn',
];
$origin = $request->header('Origin');
if (in_array($origin, $allowOrigins)) {
if ($request->isMethod('OPTIONS')) {
$response->header('Access-Control-Allow-Origin',$origin);
// $response->header('Access-Control-Allow-Credentials','true');
$response->header('Access-Control-Allow-Methods','POST, GET, PUT, DELETE');
$response->header('Access-Control-Allow-Headers','Content-Type,Authorization');
$response->header('Access-Control-Max-Age','86400');
}else{
$response->header('Access-Control-Allow-Origin', $origin);
// $response->header('Access-Control-Allow-Credentials','true');
}
}
return $response;
}
}