2019-03-05
서로 다른 도메인 끼리 AJAX통신을 하려면 CORS(Cross-Origin Resource Sharing)설정이 되어 있어야 한다.

CORS를 지원하려면 요청받는 서버에는 해더 설정을, 요청하는 서버에는 withCredentials설정을 해야 한다.

요청받는 서버
header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS');

요청하는 서버
$.ajax({
	type: "get"
	,url: "http://b1ix.net"
	,xhrFields: {
		withCredentials: false
	}
	,success: function(msg){
	}
	,error: function(xhr, option, error){
		console.log(xhr, option, error);
	}
});
위 부분을 laravel에서 사용할때는 미들웨어(middleware)로 만들어서 사용하면 편리하다

$ php artisan make:middleware CORS Middleware created successfully.
우선 CORS라는 미들웨어를 하나 만든다. 그리고 function hanlde 부분에 header를 넣어준다
public function handle($request, Closure $next)
{
	header('Access-Control-Allow-Origin: *');
	header('Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS');
	return $next($request);
}

다음으로 app\Http\Kernel.php 파일에 가서 cors라는 이름으로 middleware를 등록해준다
protected $routeMiddleware = [
.
.
.
	'cors' => \App\Http\Middleware\CORS::class,
];

마지막으로 routes\web.php에 가서 middelware를 추가해 준다
Route::middleware(['cors'])->group(function(){
	Route::get('/ajax_test/{id}', 'Main\TestController@index')->where('id', '[0-9]+');
});

이제 요청하는 서버에서 ajax 요청시에 xhrField의 withCredentials설정만 잘 해서 보내면 된다.


//------------------ 2019.06.18 추가사항

서로다른 도메인끼리 AJAX통신을 할때, 서로의 COOKIE를 공유하기를 원한다면, withCredentials 옵션을 true로 해놓아야 하고, Access-Control-Allow-Origin 옵션에서 해당 도메인을 정확히 지정해 주어야 한다.
아래의 예제를 보자.

서버 - aa.b1ix.net
public function handle($request, Closure $next)
{
	header('Access-Control-Allow-Origin: http://bb.b1ix.net');
	header('Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS');
	header('Access-Control-Allow-Credentials: true');
	return $next($request);
}
클라이언트 - bb.b1ix.net
$.ajaxSetup({
	headers: {
		'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
	}
});

$.ajax({
	type: "post"
	,url: "http://aa.b1ix.net"
	,xhrFields: {
		withCredentials: true
	}
	,success: function(msg){
	}
	,error: function(xhr, option, error){
		console.log(xhr, option, error);
	}
});
위의 예제처럼 서버와 클라이언트 모두 Credentials값이 true 여야 하고, Access-Control-Allow-Origin에 요청을 하는 클라이언트 주소가 명시 되어 있으면, 둘 사이에는 COOKIE가 공유된다.
laravel에서 서로다른 도메인간에 인증을 유지하려면 위와 같이 설정을 하면 된다.
위의 예제는 method를 post로 했기 때문에 csrf토큰을 지정해주는 부분도 추가해서 예제로 넣었다.


//------------------ 2020.02.07 추가사항

서로 다른 도메인 끼리 AJAX 통신을 할때, 서버는 laravel로 만들어 졌지만, 클라이언트는 아닐 경우는 아래와 같이 해야 서로 통신이 가능하다.

서버 - aa.b1ix.net
// 다중 CORS 지정하는 부분
$r_url = isset($_SERVER['HTTP_ORIGIN'])?$_SERVER['HTTP_ORIGIN']:'';
$allow_url = [
	'http://bb.b1ix.net',
	'http://cc.b1ix.net',
	'http://dd.b1ix.net',
];

if( array_search($r_url, $allow_url) !== false ){
	header('Access-Control-Allow-Origin: '.$r_url);
}

header('Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS');
header('Access-Control-Allow-Credentials: true');
Access-Control-Allow-Origin옵션엔 1개의 url만 적용이 된다. 그래서 여러곳에서 요청할 경우엔 위의 소스처럼 해야 한다.

서버 - aa.b1ix.net - web.php
// web.php에 csrf_token을 뿌려주는 페이지 만들기
Route::middleware(['cors'])->group(function(){
	Route::get('/csrf_token', function(){
		return csrf_token();
	});

	Route::post('/tmp_api_1', 'Main\ApiController@tmp_api_1');
});
위에서 만든 cors미들웨어를 사용하여, csrf_token()을 가져올 수 있는 GET방식 route를 하나 만든다.

클라이언트 - cc.b1ix.net
function get_csrf(callback){
	$.ajax({
		type: 'get'
		,url: 'http://aa.b1ix.net/csrf_token'
		,data: ''
		,xhrFields: {
			withCredentials: true
		}
		,success: function(data){
			console.log(data)
			callback(data) // 받아온 csrf_token을 반환해주는 부분
		}
		,error: function(xhr, status, msg){
			console.log(xhr)
		}
	});
}

function test_ajax1(csrf_token){
	var tmp1 = {
		tmp_val1:'test_ajax1',
		'_token': csrf_token //이부분에서 '_token'이라는 key로 csrf_token값을 전달해 주어야 한다
	}

	$.ajax({
		type: 'post'
		,url: 'http://aa.b1ix.net/tmp_api_1'
		,data: tmp1
		,xhrFields: {
			withCredentials: true
		}
		,success: function(data){
			data = JSON.parse(data)
			console.log(data)
		}
		,error: function(xhr, status, msg){
			console.log(xhr)
		}
	});
}

$(function(){
	get_csrf(function(csrf_token){
		test_ajax1(csrf_token)
	})
})
위처럼 설계 하면, 클라이언트가 laravel로 되어 있지 않아도, csrf_token을 가지고 AJAX통신을 할 수 있다.
물론 실제로 사용 될 때는 위의 예제에서 로그인 인증까지 포함된 채로 사용 되는 경우가 더 많을 것이다.