본문 바로가기
기타

[CORS]access-control-allow-origin 오류 해결방법

by 코딩공장공장장 2020. 11. 13.

스프링 프로젝트를 aws 서버에 호스팅까지하고 https 적용까지하며 

 

웹 프로젝트를 진행하고 테스트 하는 도중 safari 브라우저에서 웹소켓 통신이 되지 않음을 발견했다.

 

sockjs와 stomp를 통해 채팅 프로그램을 구현했고 이를 브라우저마다 테스트 하는 도중 safari에서 

 

 

 

Unexpected response code: 426

XMLHttpRequest cannot load https://url주소.com/hello/.../.../../생략     Origin  is not allowed by Access-Control-Allow-Origin.

SECURITY_ERR: DOM Exception 18 : An attempt was made to break through the security policy of the user agent.

Failed to load resource : 서버에 연결할 수 없습니다. ..........등등등(생략) 

 

 

위와 같은에러가 나타났다 구글링을 통해 Access-Control-Allow-Origin 에러를 검색해보니 

 

많이 나오는 단어가 CORS 정책이다. 

 

 

CORS

 

cors는 Cross-Origin Resource Sharing 의 약자로 직역하면 교차 출처 자원 공유라는 말이지만 직역하면 

 

말이 너무 어렵고 단순하게 요청을 보내는 도메인과 다른 도메인을 요청하는 경우에 이에 대한 권한을 가진

 

도메인만 접근할수 있도록 한 정책입니다. 

 

즉, 서버에서 우리가 요청을 허용할 도메인을 지정해줘야합니다. 

 

구글링을 하다보면 cors와 관련된 많은 포스팅과 대표적인 에러 처리 방법에 대해 많이 나와 있을 것입니다. 

 

저는 특이하게 sockjs와 stomp환경의 웹소켓 통신시 다른 브라우저는 모두 잘되었지만 사파리 브라우저에서만 작동이 

 

되지 않았습니다. 구글링을 해보아도 저와 같은 에러를 겪으신 분인 없었고 cors정책을 해결한 이후에도 

 

수많은 에러들이 나타나서 저와 같은 에러를 겪으신 분들을 위해 좀 더 자세하게 알려드리도록 하겠습니다. 

 

 

 

오류 해결 

 

먼저, 저는 스프링 4.0.3 스프링 시큐리티 3.2.4 환경에서 프로젝트를 개발해왔습니다. 

 

구글링에 있는 대부분의 자료는 스프링 4.3 시큐리티4.1 이상의 버전이어야 적용할 수 있는 방법이었습니다. 

 

저 또한 여러 방법을 시도해보다 안되었고 결국 스프링 4.3과 시큐리티 4.1로 버전 마이그레이션을 하였습니다.

 

참고로 스프링 마이그레이션은 자바를 1.7 에서 1.8로 변경시켜주었고 servlet은 3.0을 그대로 진행하여 크게 달라지는게

 

없었지만 스프링 시큐리티의 경우 3버전과 4버전 달라지는 부분이 꽤 있으니 마이그레이션시 주의 해주세요.

 

to-dy.tistory.com/76

 

Spring Security 4.x 버전에서 변경된 점 확인하기

Spring Security 3.x 버전에서 Spring Security 4.x 버전으로 올라가면서 변경된 것들이 있다. 스프링 시큐리티 3 버전으로 공부를 하다가 까먹기전에 정리글을 쓰면서 공부하자는 생각에 새로운 프로젝

to-dy.tistory.com

 

위의 출처에 나와 있는것만으로도 충분히 도움이 됩니다. 다만 안나온게 하나 있는데

 

csrf에 대한 설정이 나와 있지 않습니다. 

 

시큐리티 xml 파일의 <security:http>태그 안에 <sec:csrf disabled = "true" /> 를 설정해주세요.

(csrf설명은 주제를 넘어가므로 생략)

 

버전 마이그레이션이 끝났으니 이제 본격적으로 오류를 해결해보도록 하겠습니다. 

 

 

 

 

아래와 같이 WebsocketConfig 클래스 아래에서 setAllowedOrigins("*")를 추가해줌으로써 

 

모든 도메인 요청을 허락하도록 지정합니다. 와일드카드(*)를 통해 모든 요청을 허락했지만 실제로 사용할때는 

 

반드시 사용하는 요청만 지정하도록 해주세요. 

 

 

@Configuration
@EnableScheduling
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer implements SchedulingConfigurer  {

	
    public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
        stompEndpointRegistry.addEndpoint("/hello").setAllowedOrigins("*").withSockJS();
    }

 

 

자 이렇게 다시한번 서버를 구동하고 테스트를 해보겠습니다. 

 

그랬더니 

 

POST http://url주소/hello/.../../생략 403 (Forbidden)

Error: Incompatible SockJS! Main site uses: "1.1.5", the iframe: "1.0.0".

GET  http://url주소/hello/.../../생략 500(Internal Server Error)

Refused to display document because display forbidden by X-Frame-Options.

 

아까와 다르게 access-control-allowed-origin 에러는 나타나지 않지만 위와 같은 새로운 에러가 나타났습니다. 

 

 

 

하나씩 해결해보겠습니다. 

 

 

 

 

 

 

 

 

 

먼저 Error: Incompatible SockJS! Main site uses: "1.1.5", the iframe: "1.0.0". 이 에러를 해결하기 위해

 

밑에처럼  setClientLibraryUrl(""); 메소드에 자신이 사용하는 sockJS url 주소를 넣어주세요.

 

 

@Configuration
@EnableScheduling
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer implements SchedulingConfigurer  {

	
    public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
        stompEndpointRegistry.addEndpoint("/hello").setAllowedOrigins("*").withSockJS()
        .setClientLibraryUrl("https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.1.5/sockjs.min.js");
    }

 

 

자 그러고 나서 서버를 구동하고 테스트를 하면

 

 

POST http://url주소/hello/.../../생략 403 (Forbidden)

Refused to display document because display forbidden by X-Frame-Options.

GET  http://url주소/hello/.../../생략 500(Internal Server Error)

Refused to display document because display forbidden by X-Frame-Options.

 

 

이제 Error: Incompatible SockJS! Main site uses: "1.1.5", the iframe: "1.0.0". 에러는 해결이 되었습니다.

 

 

 

 

Refused to display document because display forbidden by X-Frame-Options. 에러를 해결해보도록 하겠습니다.

 

security 설정 파일에서

 

 

<sec:headers>
          <sec:frame-options disabled="true"></sec:frame-options>
</sec:headers>

 

 

위 코드를 추가해줍니다. 참고로 http태그 안에 넣어주시면 됩니다 .

 

 

 

자 이제 남은건

 

POST http://url주소/hello/.../../생략 403 (Forbidden)

GET  http://url주소/hello/.../../생략 500(Internal Server Error)

 

 

위 오류만 해결해주면 됩니다. 서버 콘솔을 확인해보니

 

 

심각: Servlet.service() for servlet [dispatcher] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.socket.sockjs.SockJsException: Uncaught failure in SockJS request, uri=http://localhost:8080/hello/603/ooediex0/jsonp?c=_jp.axuukq4; nested exception is org.springframework.web.socket.sockjs.SockJsTransportFailureException: Failed to open session; nested exception is java.lang.IllegalArgumentException: Async support must be enabled on a servlet and for all filters involved in async request processing. This is done in Java code using the Servlet API or by adding "<async-supported>true</async-supported>" to servlet and filter declarations in web.xml. Also you must use a Servlet 3.0+ container] with root cause

java.lang.IllegalArgumentException: Async support must be enabled on a servlet and for all filters involved in async request processing. This is done in Java code using the Servlet API or by adding "<async-supported>true</async-supported>" to servlet and filter declarations in web.xml. Also you must use a Servlet 3.0+ container

at org.springframework.util.Assert.isTrue(Assert.java:68)

at org.springframework.http.server.ServletServerHttpAsyncRequestControl.<init>

 

 

위와 같이 에러내용을 확인할 수 있었습니다. 굵게 표시 된 <async-supported>true</async-supported>태그를 보면

 

web.xml에 태그를 서블릿과 필터마다 추가해주라고 알려주고 있습니다. 

 

태그를 서블릿과 필터마다 추가해주면 됩니다.

 

 

<servlet>
		<servlet-name>dispatcher</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>
				/WEB-INF/lesson.xml
			</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
		<async-supported>true</async-supported>
	</servlet>
     
     <servlet-mapping>
		<servlet-name>dispatcher</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
     
	<filter>
		<filter-name>springSecurityFilterChain</filter-name>
		<filter-class>
			org.springframework.web.filter.DelegatingFilterProxy
		</filter-class>
		<async-supported>true</async-supported>
	</filter>
	<filter-mapping>
		<filter-name>springSecurityFilterChain</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	
	

	<filter>
		<filter-name>encodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<async-supported>true</async-supported>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
	</filter>
	
	<filter-mapping>
		<filter-name>encodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	
	
	<filter>
    <filter-name>sitemesh</filter-name>
    <filter-class>
        com.opensymphony.module.sitemesh.filter.PageFilter
    </filter-class>
    <async-supported>true</async-supported>
	</filter>

	<filter-mapping>
  	  <filter-name>sitemesh</filter-name>
   	 <url-pattern>/*</url-pattern>
	</filter-mapping>
	
	
	<filter>
		<filter-name>deviceResolverRequestFilter</filter-name>
		<filter-class>org.springframework.mobile.device.DeviceResolverRequestFilter</filter-class>
		<async-supported>true</async-supported>
	</filter>

 

 

자, 보시면 필터마다 태그가 다 추가됨을 알수있습니다. 중요한건 모든 필터마다 다 추가해주어야 하는것입니다. 

 

저 또한 이 필터는 관련 없는거 같은데 추가해줘야되나 생각이 들어 서블릿과 시큐리티 필터에만 추가를 했었는데

 

다 적용해줘야 에러가 해결됩니다.

 

 

반응형