ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [20] Spring Web MVC : CORS
    Spring/Spring boot 2020. 7. 4. 08:26
    반응형

    [CORS]

    Single-Origin Policy[각주:1]에 의해 하나의 origin[각주:2]에서 다른 origin의 resource에 대해 request 할 수 없습니다. 즉, http://localhost:8080에서 사용되고 있는 Rest API를 http://localhost:18080와 같이 다른 origin에서 AJAX 호출 등을 통해 request 할 수 없게 됩니다.

     

    Cross-Origin Resource Sharing (CORS)은 Single-Origin Policy를 우회하기 위해 web browser가 지원하는 기술입니다. CORS를 통해 서로 다른 origin 간에 resource를 sharing 할 수 있도록 해줍니다.

     

     

     


    [CORS 사용의 예]

    Spring에서 CORS 기능을 사용하기 위해 해야 하는 여러 가지 bean 설정을 Spring boot가 모두 진행합니다. 사용자는 CORS 기능을 사용하기 위해 @CrossOrigin annotation을 붙이기만 하면 됩니다.

     

    CORS를 사용하는 예를 들기 위해 우선 server역할을 할 project(이하 Server Project)와 SOP를 우회하여 resource를 요청할 client역할의 project(이하 Client Project)를 생성합니다.

     

     

    Step 1

    Server Project는 간단한 RestAPI를 제공하도록 작성합니다.

    @SpringBootApplication
    @RestController
    public class SpringCrossServerApplication {
    
        @GetMapping("/hello")
        public String hello() {
            return "Hello";
        }
    
        public static void main(String[] args) {
    
            SpringApplication app = new SpringApplication(SpringCrossServerApplication.class);
            app.run(args);
        }
    }

     

     

     

    Step2

    Client Project는 application.properties file에 다음 property를 추가하여 18080 port에서 실행되도록 합니다. 이로써 Server Project와 Client Project는 다른 origin이 됩니다.

    server.port=18080

     

    /src/resources/static/index.html file을 생성하여 Server Project의 Rest API를 비동기 호출하도록 code를 작성합니다. 이 code를 통해 cross origin하게 Server Project의 resource에 대해 성공적으로 response가 돌아오면 alert 창으로 message를 출력하고 실패하면 fail을 출력하도록 하겠습니다.

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    CORS Client
    </body>
    <script src="/webjars/jquery/3.5.1/dist/jquery.min.js"></script>
    <script>
        $(function() {
            $.ajax("http://localhost:8080/hello")
                .done(function(msg) {
                    alert(msg);
                })
                .fail(function() {
                    alert("fail");
                });
        })
    </script>
    </html>

     

    이 code에는 jQuery를 통해 비동기 호출을 하고 있으므로 관련된 의존성을 추가해줘야 정상적으로 동작합니다. Client Project의 pom.xml을 열으 <dependencies></dependencies> tag 안에 jQeury 의존성 관리를 위한 code를 추가합니다.

    <dependencies>
    	
        ...
        
        <!-- https://mvnrepository.com/artifact/org.webjars.bower/jquery -->
        <dependency>
        	<groupId>org.webjars.bower</groupId>
        	<artifactId>jquery</artifactId>
    	    <version>3.5.1</version>
        </dependency>
        
        ...
    
    </dependencies>

     

     

    Step 3

    browser 주소창에 http://localhost:18080/을 입력하여 Client Project의 index.html을 reqeust 합니다.

    이 reqeust는 Single-Origin Policy에 위배되며 아직 CORS를 사용하고 있지 않기 때문에 alert 창으로 fail이라는 message를 보게 됩니다.

     

    개발자 도구의 console 창을 통해 확인해보면 서로 다른 origin으로 request 할 때 CORS policy에 의해 blocking 하고 있는 것을 볼 수 있습니다. CORS를 사용하여 다른 origin에 reqeust 하기 위해서는 (Server Project의) resource에  'Access-Control-Allow-Origin' header가 추가되어야 한다고 합니다.

     

    Step4

    'Access-Control-Allow-Origin' header를 추가하기 위해서는 Server Project의 Rest API에 @CrossOrigin annotation을 붙여줍니다. 변경된 code는 다음과 같습니다.

    @SpringBootApplication
    @RestController
    public class SpringCrossServerApplication {
    
        @CrossOrigin(origins = "http://localhost:18080")	//'Access-Control-Allow-Origin' header 추가
        @GetMapping("/hello")
        public String hello() {
            return "Hello";
        }
    
        public static void main(String[] args) {
    
            SpringApplication app = new SpringApplication(SpringCrossServerApplication.class);
            app.run(args);
        }
    }
    

     

    이제 다시 browser에서 http://localhost:18080/ 으로 reqeust를 던지면 기대했던 message를 alert창으로 출력하는 것을 확인할 수 있습니다.

     

     

    tip
    test 중 web application이 종료되지 않은 상태에서 다시 build 후 실행하도록 하면 정상적으로 실행이 불가합니다. 이는 기존 실행되던 web application을 종료하여 해결할 수 있습니다.

    특정 port에서 실행 중이던 web application이 종료되지 않았을 때는 다음 순서에 따라 종료합니다.

    1. terminal을 열고 명령줄에 ps ax | grep tomcat 을 입력하여 process id를 확인합니다.


    2. 명령줄에 kill -9 [process id] 를 입력하여 web application을 종료합니다.

     

     


    [CORS를 광범위로 적용하는 방법]

    1. controller 내부의 모든 resource에 대해 CORS 적용해야 할 경우.

    controller class에 @CrossOrigin을 붙여줍니다.

    @SpringBootApplication
    @CrossOrigin(origins = "http://localhost:18080")	//'Access-Control-Allow-Origin' header 추가
    @RestController
    public class SpringCrossServerApplication {
    
        @GetMapping("/hello")
        public String hello() {
            return "Hello";
        }
    
        public static void main(String[] args) {
    
            SpringApplication app = new SpringApplication(SpringCrossServerApplication.class);
            app.run(args);
        }
    }
    

     

     

    2. 여러 controller에 대해 CORS를 적용해야 할 경우

    WebMvcConfigurer interface를 구현하는 web configuration file을 만들어 여러 controller에 대해 CORS를 적용할 수 있습니다. WebMvcConfigurer의 method를 구현하면 Spring MVC에서 제공하는 기능을 그대로 사용하면서 사용자가 추가로 기능을 확장할 수 있게 됩니다. CORS의 전체 적용을 위해 WebMvcConfigurer.addCorsMappings() method를 구현합니다.

    @Configuration
    public class WebConfig implements WebMvcConfigurer {
    
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            
            //전체 resource를 허용할 경우 registry.addMapping("/**")...
            registry.addMapping("/hello")   //mapping할 resource를 지정합니다.
                    .allowedOrigins("http://localhost:18080");  //CORS를 허용할 origin을 지정합니다.
        }
    }

    이제 @CrossOrigin annotation을 사용하지 않아도 web configuration file에서 설정한 대로 지정된 resource를 특정 origin에 대해 허용합니다.

    1. 동일한 origin에만 request를 보낼 수 있는 정책 [본문으로]
    2. origin은 http 또는 https와 같은 URI schema, hostname(예: mail.naver.com에서 mail에 해당하는), port라는 세 가지를 요소를 조합한 것을 말합니다. [본문으로]

    댓글

Designed by Tistory.