waveofmymind
기록하는 습관
waveofmymind
전체 방문자
오늘
어제
  • 분류 전체보기 (124)
    • 📝 정리 (5)
    • 🌊TIL (9)
    • 💻CS (1)
      • 자료구조 (1)
    • 📙Language (9)
      • ☕Java (6)
      • 🤖Kotlin (3)
    • 🍃Spring (28)
    • 👨🏻‍💻알고리즘 (67)
      • 프로그래머스 (59)
      • 백준 (3)
    • 👷DevOps (4)
      • 🐳Docker (2)
      • 🤵Jenkins (1)

블로그 메뉴

  • 홈
  • Spring
  • Java
  • 알고리즘

공지사항

인기 글

태그

  • 스프링 부트
  • 통합테스트
  • AOP
  • 스프링
  • 스프링 시큐리티
  • JDBC
  • kotest
  • mybatis
  • 힙
  • resultset
  • 스택
  • BFS
  • til
  • spring boot
  • 트랜잭션 전파
  • Open AI
  • Spring Security
  • 챗GPT
  • spring
  • SpringAOP
  • sql
  • 완전탐색
  • 코틀린
  • LeetCode
  • chat GPT
  • kotlin
  • 트랜잭션
  • CORS
  • 다이나믹 프로그래밍
  • Connection

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
waveofmymind

기록하는 습관

[Spring Security] CORS 문제 해결하기
🍃Spring

[Spring Security] CORS 문제 해결하기

2023. 4. 1. 15:22

리액트에서 서버로 요청을 보내는데 악명 높은 CORS 문제를 만났다.

CORS란?

한 웹 어플리케이션이 다른 포트에 있는 자원에 접근하고자 할때, 접근 권한을 주어야한다.
예를 들어, React의 http://localhost:3000 에서 Spring의 http://localhost:8080으로 데이터를 보내거나 받고 싶은 경우, CORS 접근 권한이 필요한 것이다.
이는 브라우저에 구현되어 있기 때문에 Postman과 같은 툴을 이용해 API를 보내면 발생하지 않는다.

마주한 오류는 아래와 같다.

Access to fetch at 'http://localhost:8080/login' from origin 'API 호출한 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.
If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled
  • 우선 브라우저는 요청 헤더에 Origin이라는 필드에 출처를 담아서 API 메시지를 보낸다.
  • 이를 받은 서버는 응답을 할 때 Access-Control-Allow-Origin이라는 값에 리소스에 접근이 허용된 출처를 내려준다.
  • 이후 응답을 받은 브라우저는 Origin과 Access-Control-Allow-Origin 두 값을 비교해서 이 응답이 유효한지를 결정한다.

그럼 다시 위 오류 메시지를 보면 Access-Control-Allow-Origin 값을 받지 못했다는 것을 알 수 있다.

 

해당 문제를 저번에도 마주한 적이 있기 때문에

저번처럼 다음과 같은 WebMvcConfigurer의 메소드를 오버라이딩 해서 구현을 했다.

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("http://localhost:3000")
                .allowedMethods("OPTIONS", "GET", "POST", "PUT", "DELETE", "PATCH");
    }
}

JWT 토큰을 헤더에 Key 값을 Authorization 으로 보내는 방법은 동일했으나,

그러나, 이번에는 Preflight 문제를 해결하지 못했다.

Failed to load 'http://localhost:8080/login': Response to preflight request doesn't pass access control
check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'API
호출한 IP' is therefore not allowed access.
The response had HTTP status code 405.
If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
preflight란?
브라우저가 요청을 한번에 보내지 않고 예비 요청/본 요청으로 나누어 서버로 보내는 경우, 예비 요청이 preflight이다.
HTTP 메서드 중 OPTIONS가 사용된다.
이를 통해 브라우저 스스로 이 요청을 보내는 것이 안전한지를 확인한다.

 

정리하면, 내가 요청한 API에서 Authorization 헤더에 토큰 값을 담아야 하지만,

 preflight 요청에는 Authorization 헤더가 없어서 서버에서 토큰 값이 존재하지 않아 요청을 거절해서, 

결국 본 요청도 보내지지 않아서 발생한 오류인 것이다.

 

그래서 SecurityConfig에 다음과 같은 구문을 추가했다.

.antMatchers(HttpMethod.OPTIONS,"/**/*").permitAll()

 

아래와 같이 SecurityConfig를 수정했다.

@Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.httpBasic().disable()
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS,"/**/*").permitAll() //추가한 구문
                .antMatchers("/users/experts").hasRole("EXPERT")
                .antMatchers("/users/**","/experts/**").permitAll()
                .antMatchers("/answers/**").permitAll()
                .antMatchers("/questions/**").hasAnyRole("USER","EXPERT")
                .antMatchers("/users/me").hasAnyRole("USER", "EXPERT")
                .and()
                .formLogin().disable()
                .addFilterBefore(jwtAuthorizationFilter, UsernamePasswordAuthenticationFilter.class)
                .exceptionHandling()
                .authenticationEntryPoint(new JwtAuthenticationEntryPoint())
		;
        return http.build();
    }

 

OPTIONS 메소드로 오는 요청은 모두 허용시켰더니 정상적으로 해결이 됐다.

 

 

 

'🍃Spring' 카테고리의 다른 글

[Spring] WebMvcTest에서 발생하는 SpringSecurity 의존성 문제 해결하기  (0) 2023.04.19
[Redis] Redis 캐시를 사용한 JWT 리프레시 토큰 관리하기  (0) 2023.04.10
[Spring] 조회수 필드에 대해 동시성 문제 해결하기  (1) 2023.03.29
[Spring] Spring AOP를 이용한 권한 체크  (0) 2023.02.21
[Spring] Business Exception 처리하기  (0) 2023.02.21
    '🍃Spring' 카테고리의 다른 글
    • [Spring] WebMvcTest에서 발생하는 SpringSecurity 의존성 문제 해결하기
    • [Redis] Redis 캐시를 사용한 JWT 리프레시 토큰 관리하기
    • [Spring] 조회수 필드에 대해 동시성 문제 해결하기
    • [Spring] Spring AOP를 이용한 권한 체크
    waveofmymind
    waveofmymind

    티스토리툴바