* 아래 Source들을 참고하여 작성했습니다.
🔥 CORS (Cross Origin Resource Sharing)
- 사용자가 가져오는 리소스들이 안전한지 검사하는 관문
- 프론트 애플리케이션과 다른 origin(출처)를 가진 서버 애플리케이션의 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제
Access to XMLHttpRequest at ‘http://{백엔드 서버 ip주소}/member/signup’ from origin ‘http://{프론트엔드 ip 주소}’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
'https://{백엔드 서버 ip주소}'에서 오리진 'http://{프론트엔드 ip 주소}'으로 가져올 수 있는 액세스가 CORS 정책에 의해 차단되었습니다. 요청된 리소스에 'Access-Control-Allow-Origin' 헤더가 없습니다.
XMLHttpRequest는 Same-Origin 정책 (같은 출처의 리소스를 공유할 수 있다.)을 따르는데 백엔드와 프론트의 Origin이 다르다. 따라서 위 오류는 CORS를 허용해서 다른 출처 리소스 공유를 허용하라고 권하는 것이라 볼 수 있다.
여기서 Same-Origin Policy와 Origin는 무엇인지, CORS의 작동방식은 어떠한지 자세히 알아보자
🔥 Origin
URL은 다음과 같이 구성되어 있다
Origin(출처)은 Protocol + Host + Port 까지 합친 것을 의미한다.
배포 주소를 의미할 수 있다.
🔥 SOP (Same Origin Policy)
웹에는 크게 SOP(Same Origin Policy), CORS(Cross Origin Resource Sharing) 두가지 정책이 있다.
SOP는 "같은 출처에서만 리소스를 공유할 수 있다" 는 규칙을 가지 정책이다.
URL에서 Protocol, Host, Port가 모두 동일해야 Same Origin(같은 출처) 라고 할 수 있다.
이 중 하나라도 일치하지 않으면 Cross Origin(교차 출처)이 된다.
출처를 비교하는 로직은 서버가 아닌 브라우저에서 구현된 스펙이다.
따라서 CORS 정책을 위반하는 요청에 서버가 정상적으로 응답하더라도 브라우저가 이 응답을 분석해서 CORS 정책에 위반되면 그 응답을 처리하지 않는다.
위의 오류는 프론트 애플리케이션과 서버 애플리케이션의 출처가 다르므로 브라우저에서 서버의 응답을 처리하지 않고 막아버린 것이다.
하지만 프론트와 서버가 같은 origin을 가지는 경우는 많지 않다. 프론트의 크기가 커지면서 프론트는 프론트데로 별도로 관리하기 위해서 별도의 origin을 가지게 된다. 또한 보안적인 이슈로 프론트와 서버 애플리케이션을 다른 origin에 넣는 경우도 많다고 한다. (유저에게는 서버에 직접적으로 접근할 수 있는 모든 방향을 차단하는 것이 좋기 때문)
결론적으로 프론트와 서버가 서로 다른 출처를 가지는 경우가 많고, 그래서 이러한 경우에는 CORS를 통해 프론트가 다른 origin을 가지는 서버 애플리케이션의 자원에 접근할 수 있도록 해야한다.
🔥 CORS 작동 방식
👉 CORS (Cross Origin Resource Sharing)
- 교차 출처 리소스 공유
- 프론트 애플리케이션과 다른 origin(출처)를 가진 서버 애플리케이션의 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제
👉 Preflight Request (예비 요청)
- 요청을 한번에 보내지 않고, 예비요청과 본요청으로 나누어 서버에 전달하는 방식이다.
- 브라우저가 예비요청을 보내는 것을 Preflight라고 부른다.
- 예비요청은 GET, POST, PUT과 같은 메서드가 아닌 OPTIONS라는 메서드가 사용된다.
- 서버는 요청시 허용할 Origin과 Method, Headers등을 미리 지정(CORS 정책)해 둔다. 요청이 들어오면 허용할 정보들을(CORS 정책) 응답으로 보내준다.
- 브라우저는 클라이언트에서 보낸 Access-Control-Request정보들(클라이언트에서 보낸 요청), Origin과 서버의 Access-Control-Allow 정보들(서버가 응답해준 정책)을 비교하여 해당 요청이 안전한지 확인하고 본 요청을 보낸다.
- 이후 서버가 본 요청에 대한 응답을 하면 최종적으로 이 응답을 자바스크립트로 넘겨준다.
예비 요청은 보통 PUT, DELETE 같은 요청을 보낼 때 이용된다.
PUT, DELETE는 서버의 데이터를 변경하는 요청이기 때문에, 인증을 하는 것이다.
👉 Simple Request (단순 요청)
- 예비 요청을 보내지 않고 바로 서버에 본 요청을 보낸다.
- 서버가 응답의 헤더에 Access-Control-Allow-Origin을 보내주면 브라우저가 CORS 정책 위반 여부를 검사한다.
- Credential이 없는 요청의 경우 "*" 을 통해 브라우저의 Origin에 상관없이 모든 Origin을 허용할 수 있다.
- 단순 요청은 요청의 메소드가 GET, HEAD, POST 중 하나여야 한다.
- 이 밖에 수동으로 설정할 수 있는 헤더와 허용되는 Content-Type만 사용할 수 있는 조건이 있다. 따라서 대부분의 요청은 예비 요청으로 이루어진다고 한다.
👉 Credentials Request (인증 요청)
- 보안을 더 강화하고 싶을 때 사용한다.
- 브라우저가 제공하는 비동기 리소스 요청 AP인 XMLHttpRequest 객체나 fetch API는 별도의 옵션 없이 브라우저의 쿠키 정보나 인증과 관련된 헤더를 함부로 요청에 담지 않는다.
- credentials 옵션을 사용해 요청에 인증과 관련된 HTTP Cookie와 HTTP Authentication 정보를 담을 수 있게 해준다.
- credentials이 필요한 CORS에는 프론트는 Response Header에 withCredentials=true, Backend는 Response header에 Access-Control-Allow-Origin을 포함해야 한다.
- Access-Controll-Allow-Origin에는 모든 요청을 허용하는 *를 사용할 수 없으며, 명시적인 URL이어야 한다.
- 서버는 Access-Control-Allow-Credential: true로 응답해 주어야 한다. (서버에서 credentials를 true로 설정해주어야 한다.)
- 만약 클라이언트의 요청에 의해서 서버가 헤더에 응답 값을 보내줄 때, 클라이언트가 그 응답 헤더 값에 접근할려면 서버에서 접근 허용할 헤더를 추가해줘야한다.
🔥 Spring Security에서 CORS 설정
👉 CorsFilter 를 Bean으로 등록
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
//허용할 url 설정
configuration.addAllowedOrigin("http://localhost:3000");
//허용할 헤더 설정
configuration.addAllowedHeader("*");
//허용할 http method
configuration.addAllowedMethod("*");
// 클라이언트가 접근 할 수 있는 서버 응답 헤더
configuration.addExposedHeader(TokenProperties.AUTH_HEADER);
configuration.addExposedHeader(TokenProperties.REFRESH_HEADER);
//사용자 자격 증명이 지원되는지 여부
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
👉 Security에 CorsFilter 추가
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.cors().configurationSource(corsConfigurationSource());
http.csrf().disable()
.addFilterBefore(new JwtAuthFilter(jwtUtil,userDetailsService),UsernamePasswordAuthenticationFilter.class)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests((authz)->authz
.antMatchers("/auth/**").authenticated()
.anyRequest().permitAll());
return http.build();
}
Source
https://medium.com/@su_bak/cors%EB%9E%80-f7e1447e97d8
https://velog.io/@chullll/Spring-Security-CORS
https://cordingmonster.tistory.com/101
'Web' 카테고리의 다른 글
[HTTP 웹 기본 지식] 섹션4. HTTP 메서드 (0) | 2022.11.17 |
---|---|
[HTTP 웹 기본 지식] 섹션3. HTTP 기본 (0) | 2022.11.17 |
[HTTP 웹 기본 지식] 섹션2. URI와 웹 브라우저 요청 흐름 (0) | 2022.11.16 |
[HTTP 웹 기본 지식] 섹션1.인터넷 네트워크 (0) | 2022.11.04 |
[Web] Restful API (0) | 2022.08.21 |