안그래보이지만 개발자/백엔드

Spring CORS 구현 / XMLHttpRequest 요청

자네트 2019. 5. 3. 23:39
반응형

회사에서 하고 있는 프로젝트와 관련하여 CORS 이슈가 생겼다.

해결하고 보니 그리 어려운 문제는 아니었으나 모르고 보면 어려울 수도 있는 문제이고

나도 어차피 매뉴얼을 작성해야 하므로 공부하는 겸사겸사 해결 과정을 정리해본다.

내 환경에 맞는 적용법이므로 환경이 다른 자는 다른 방법을 참고 바람,,,

폐쇄망에서 작업한 후 인터넷망에서 기록하므로 스크린샷은 없다.

 


 

환경은 다음과 같다.

개발 환경 : Spring 5.1.5, Tomcat 8.5

테스트 : 인터넷 익스플로러 / IE 11, 크롬 Chrome 72.0.x, 자체 서명 인증서

서비스 목표는 다음과 같다.

API 서버와는 다른 도메인에서  API 서버로 POST 요청을 보내고 응답을 받는다.

웹앱을 서버에 올려두고 로컬에서 요청을 던졌다.

테스트는 postman, ie 11, 크롬을 이용했다.

 

 

문제 1.

먼저 HTTP/POST요청의 경우 포스트맨에서 정상 작동했으나

HTTPS 요청에서 포스트맨이 could not get any response 오류를 띄웠다.

해결.

자체 서명된 인증서를 사용하고 있어서 발생한 문제였다.

포스트맨이 친절히 오류 원인을 알려준다. 찬찬히 오류 내용 아래 해결책을 읽어보면 금방 해결된다.

포스트맨 Settings > General 에서 SSL certificate verification 세팅을 꺼주면 https 요청도 잘 갔다가 온다.

 

 

문제 2. (포스팅 목적)

포스트맨이 아닌 브라우저에서 api 서버로 ajax 요청을 보내는데 오류가 발생한다.

cors 정책 때문이란 거 알겠고 스프링에서 적용 해줬는데 익스플로러 놈만 오류를 띄웠다.

IE 11에서 크로스 도메인 문제를 해결해줬는데도 https 요청이 막히는 문제를 해결하고자 한다.

 

과정. 해결법만 보고 싶은 사람은 스크롤 쭉쭉 내려갈 것.

CORS (Cross-Origin Resource Sharing) 개념을 알아야 한다.

자바스크립트는 다른 도메인의 서버에 요청을 보내는 것을 정책적으로 막는다. (동일 출처 정책 Same Origin Policy)

이를 해결하기 위한 게 CORS이다. 크로스 도메인 요청을 허용할지 말지 선택할 수 있다.

스프링 4.2부터 CORS를 위한 설정을 간단하게 해 주는데, 우리는 스프링 5 버전을 사용하므로 거리낌 없이 이 방법을 선택했다.

4.2 아래까지는 다른 방법 사용해야 함. 난 안 썼으므로 적지는 않겠음.

 

그런데 스프링에서 크로스오리진 어노테이션을 적용했는데도 익스플로러 11은 HTTPS 요청에서 오류를 냈다.

withCredentials 플래그를 이용한 설정도 시도해봤지만, 이 방법은 허용해 줄 도메인을 특정해줘야 하는 단점이 있다.

그런데 우리는 api 서버이므로... 요청이 들어올 도메인을 특정하면 안 된다. (아마)

그래서 이 방법은 기각.

이외의 별의별 헤더 설정(Access-Control- 어쩌구) 해줘봤으나 실패. 기각.

 

삽질의 삽질 끝에 IE 11 브라우저 설정을 건드렸더니 무려 개발자 도구 콘솔 창에 뜨는 오류 메세지가 바뀌었다.

오류 0x2ee4, 00002ee4 코드 두 개가 떴다. (원래는 이거 아니고 그냥 access denied 오류였음)

 

자체 서명 인증서 때문인 것 같다는 심증은 있었으나 해결법을 못 찾던 와중,

POST 요청을 보내기 전에 GET 요청을 한 번 보내면 해결된다는 jonny reeves 씨의 포스팅 발견.

서버로 https / get 요청을 보내자 못 보던 경고문이 떴다.

보안 경고 : 신뢰 여부를 결정한 적이 없는 회사에서 발급한 보안 인증서…

계속하시겠습니까 ?

당연히 예를 누른다.

다시 POST 요청을 보내본다.

응답이 온다.

해결 완료. 

 

 

해결방법.

자세한 설명은 위 과정에서 기술했으므로 여기는 단순 방법만 나열한다.

스프링 Spring 4.2 이상에서 CrossOrigin 어노테이션 적용 :

컨트롤러 클래스나 각각의 컨트롤러에 @CrossOrigin(origins="*") 어노테이션을 적용하면 된다.

와일드카드(*)는 모든 도메인에서의 요청을 허용하겠다는 뜻이다.

특정 도메인명을 적어주어도 된다. 나는 안 적었다.

@CrossOrigin(origins="*")
@Controller
public class ApiController{
	...
}

 

인터넷 익스플로러 IE 11 에서 https post 메소드로 xmlhttprequest 요청이 거부될 때 :

인터넷 옵션 - 보안 - 인터넷 탭에서 사용자 지정 수준 - "기타" 하위 항목에서 도메인 간 데이터 관련 항목 "사용"으로 변경

브라우저 다시 시작 후 (나는 컴퓨터를 재부팅했다 그러라고 설정 창에 써있길래)

api 서버로 post 대신 get 요청을 최초 한 번 보낸다. 내용은 없어도 상관 없다.

XMLHttpRequest 보내기

var xhr = new XMLHttpRequest();
xhr.open('GET', 'api uri 적는 곳', true);
xhr.send(null); 

인터넷 익스플로러가 띄우는 보안 경고 - 예 선택

다시 post 방식으로 요청한다. post 방식으로 보내는 xmlhttprequest 코드는 생략한다.

post 요청 보내기 성공.

 

 

더 좋고 근본적인 해결법이 있다면 알려주시면 감사합니다.

반응형