Web/PHP

[라라벨] 미들웨어를 통한 CORS 허용하기

_sparrow 2020. 3. 26. 22:54
반응형

목표

CORS를 허용하지 않았다면 same-origin Policy 정책에 근거하여 CORS 문제가 발생합니다.

CORS를 허용이 되었는지 확인하다 보면 콘솔 창에서 엄청나게 보게 될 아래의 문구..

서로 다른 도메인(주소)은  AJAX통신을 하기위해서 CORS(Cross-Origin Resource Sharing) 설정이 필요합니다.

특히 라라벨은 CSRF를 적용되어있어 토큰이 없으면 비동기 통신이 불가능합니다.

하나의 도메인에서 라라벨로 프론트 백엔드 둘 다 작업한다면 라라벨에서 제공해주는 {{ csrf_token() }} 같이 프론트에서 바로 토큰 생성해서 보내줄 수 있겠지만 이 글은 다른 도메인끼리 통신할 때 해결법입니다.

 

 

환경

백엔드 : php 7.3,  laravel framework 5.5 

프론트 : php 사용하지 않음 (너무 당연한 소린걸?)

 

 

STEP1. 미들웨어에 CORS.php 파일 만들기 및 설정 (백엔드)

// CMD내부 라라벨 파일에서 파일 생성입력(클래스 설정 역시 자동으로 다 만들어줌)
php artisan make:middleware CORS

// 위와같이 만들지않고 파일을 app/http/Middleware 내부에 직접 파일 만드셔도 됩니다.
# CORS내부 파일 설정

public function handle($request, Closure $next)
{
     header('Access-Control-Allow-Origin: *'); 
     header('Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS');
     header('Access-Control-Allow-Credentials: false'); 
        
     return $next($request);
}

 

설명

어떤 url을 허용할 것인지에 대한 헤더
header('Access-Control-Allow-Origin: *'); 
        

어떤 요청 메서드를 허용할 것인지
header('Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS');
        
인증정보를 포함한 요청 ex) 쿠키
header('Access-Control-Allow-Credentials: false'); 

 

 

 

커널 파일 설정 

app/Http 디렉터리 내부의 kernel.php를 여시고 파일 내부 하단의 $routeMiddleware 내부에 코드 작성해주시면 됩니다.

# CORS 클래스 파일을 'cors'로 간략화해주는겁니다. 나중에 왜 앞에 따로 설정했는지 확인할수있습니다..

protected $routeMiddleware = [ 

'cors' => \App\Http\Middleware\CORS::class,
:
:(기존의 클래스들)
:
];

 

라우터 설정

# 라우터 내부에 선언해주면됩니다.(web.php)
# url 참조할때 cors와 상관없이 기존의 url과 동일하게 사용하던대로 사용하면 됩니다.

Route::middleware(['cors'])->group(function(){
    Route::get('/csrf_token', function(){
        return csrf_token();
    });
    Route::post('example','Controller@example');
});

 

설명

csrf_token을 가져오기(CORS 허용)
Route::get('/csrf_token', function(){
        return csrf_token();
 });

 

토큰과 함께 post 요청(CORS)

Route::post('/example','Controller@example');

첫 번째 인자는 url주소이고 두 번째는 어떤 컨트롤러를 참조할 것인지입니다.

 

 

CSRF 보호로부터 회피하기 위해서 VerifyCsrfToken 파일에 코드를 넣어줘야 합니다.

작성을 안 하면  get방식으로 된 토큰가져오기는 되는데 post 전송이 안됩니다.

# App/Http/Middleware 디렉터리 내부의 VerifyCsrfToken 폴더 내부에 작성하면 됩니다.

    protected $except = [
        '/example'
    ];

$except 내부에 post로 전송할 url 주소를 넣어주면 됩니다.

 

 

STEP2. AJAX 전송하기 (프론트)

// 코드 작성전 헤더부분에 jquery import해줍시다.
<script src="https://code.jquery.com/jquery-3.4.1.js"></script>

<script>
//토큰 가져오기
    function get_csrf(callback){
        $.ajax({
            type: 'get'
            ,url: 'http://example.com/csrf_token'
            ,data: ''
            ,xhrFields: {
                withCredentials: false
            }
            ,success: function(data){
                console.log(data)
                callback(data) // 받아온 csrf_token을 반환해주는 부분
            }
            ,error: function(xhr, status, msg){
                console.log(xhr)
            }
        });
    }

// ajax 통신하여 post전송
    function ajax_example(csrf_token){
		
        var data = $('#value').val();
		//입력한 값
        
        var object = {
            '_token': csrf_token,
            'data':data,
        }

        $.ajax({
            type: 'post'
            ,url: 'http://example.com/example'
            ,data: object
            ,xhrFields: {
                withCredentials: false
            }
            ,success: function(data){
                
                data = JSON.parse(data); //json 해체
				
                console.log(data);
            }
            ,error: function(xhr, status, msg){
                console.log(xhr);
            }
        });
    }

    function get_token(){
        get_csrf(function(csrf_token){
            ajax_example(csrf_token)
        })
    }
</script>

<input type="text" id ="value" placeholder="값">
<input type="button" onclick="get_token();">

값을 입력 후 버튼을 누르면 get_token() 함수 내부의 get_csrf함수가 실행되고 백엔드로부터 토큰 값을 받아옵니다.

그 후 ajax post방식으로 값을 전송할 때 토큰을 보내고 싶은 값과 함께 넣어서 전달해주면 됩니다. 그리고 전송하고 리턴 값을 받게 되면 프론트에서 재차 처리하고 싶은 코드를 작성하면 됩니다.

 

 

수정) VerifyCsrfToken 파일에서 except에 등록하는 건 csrf토큰이 없어도 허용하겠다는 것이다.

그래서 토큰이 존재하는지 안 하는지와 관계없다.

서버와 서버끼리의 통신에서 토큰으로 어떻게 인증할지에 대해서 좀 더 공부해봐야겠다.

하나의 서버에서 작업에서는 위의 코드가 되긴 하지만 라라벨에서 제공하는 {csrf_token} 사용하는 게 더 편할 거다.

반응형