스프링 부트 활용

CORS

   CORS는 Cross-Origin Resource Sharing(서로 다른 origin끼리 리소스를 공유할 수 있는 기능을 제공하는 표준)의 약자이다.

SOP1를 우회하기 위한 기술로 CORS, SOP 둘 다 웹 브라우져가 제공하는 표준 기술이다. 기본값으로는 SOP가 적용되어 있다.

💡 여기서 Origin이란?

아래의 3가지를 조합해서 나온 것이 하나의 origin이다.

  • URI스키마(http, https)
  • hostname(localhost 등)
  • port(8080, 18080 등)

예를 들면 SOP는 rest api를 제공하는 서버인 localhost:8080에서 그 rest api를 localhost:18080의 어플리케이션에 호출할 수 없다. localhost:18080의 웹 브라우져에서 localhost:8080의 리소스를 가져오려고 하면 SOP 정책에 위반이기 때문이다.

이걸 가능하게 해주는 표준 기술이 CORS이다.

원래 스프링 MVC에서 CORS 기술을 사용하려면 여러가지 Bean 설정을 해줘야하지만, 스프링부트는 그러한 설정을 자동으로 해준다.

예시

rest api를 만들어서 호출할 수 있는 서버를 만든다.

프로젝트를 생성해, @RestController를 붙여준다.

package com.gracenam.springcrossserver;

import ...

@SpringBootApplication
@RestController
public class SpringcrossserverApplication {

  public static void main(String[] args) {
    SpringApplication.run(SpringcrossserverApplication.class, args);
  }

}

hello를 요청하면 hello를 화면에 뿌려주게 만든다.

@SpringBootApplication
@RestController
public class SpringcrossserverApplication {

  @GetMapping("/hello")
  public String hello() {
    return "hello";
  }

  public static void main(String[] args) {
    SpringApplication.run(SpringcrossserverApplication.class, args);
  }

}

간단한 rest api가 완성되었다. 8080포트를 쓰는 서버를 만든 것이다. 이제 이것을 요청하는 다른 애플리케이션(rest api를 사용하는 클라이언트 애플리케이션)을 만들어주자.

👉 이때 만들어준 8080 서버는 실행중인 상태이다.

package com.gracenam.springcrosclient;

import ...

@SpringBootApplication
public class SpringcrosclientApplication {

  public static void main(String[] args) {
    SpringApplication.run(SpringcrosclientApplication.class, args);
  }

}

새로 만든 클라이언트 애플리케이션은 18080으로 포트를 변경해준다. application.properties에 server.port=18080을 추가해주면 된다.

server.port = 18080

그리고 static 아래에 index 페이지를 생성한다.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <h1>CORS Client</h1>
</body>
</html>

이제 ajax call을 사용하여 서버쪽에서 만든 rest api를 호출해보자.

pom.xml에 jQuery 의존성을 추가해준다.

<dependency>
  <groupId>org.webjars.bower</groupId>
  <artifactId>jquery</artifactId>
  <version>3.4.1</version>
</dependency>

18080포트의 index 페이지에 jquery를 추가해주고, ajax로 8080포트의 hello를 요청한다.

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

이대로 18080포트를 실행시켜보면, SOP에 위배되기 때문에 fail이 나오는 것을 볼 수 있다.

이를 해결해주기 위해 서버(8080포트) 프로젝트에 @CrossOrigin 어노테이션을 붙여준다.

@SpringBootApplication
@RestController
public class SpringcrossserverApplication {

  @CrossOrigin(origins = "http://localhost:18080")
  @GetMapping("/hello")
  public String hello() {
    return "hello";
  }

  public static void main(String[] args) {
    SpringApplication.run(SpringcrossserverApplication.class, args);
  }

}

이제 다시 실행시키면 제대로 출력되는 것을 볼 수 있다.

만약 여러 Controller에 걸쳐서 설정을 해야한다면, 설정파일을 만들어서 해줄 수 있다.

먼저 web관련된 설정파일을 만든다.

package com.gracenam.springcrossserver;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

}

addCorsMapping을 override해서 Mapping해줄 것과 허용해줄 origin을 설정해준다. 만약 모든 mapping을 다 허용해주고 싶다면 /hello 대신, /** 로 적어주면 된다.

package com.gracenam.springcrossserver;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

  @Override
  public void addCorsMappings(CorsRegistry registry) {
    registry.addMapping("/hello")
            .allowedOrigins("http://localhost:18080");
  }

}

Reference


1 : Single-Origin Policy. 같은 Origin에만 요청을 보낼 수 있다.