CORS 해결, 응답 헤더 내용 누락 (Authorization) 해결 (Spring)

DongHyun Kim·2023년 6월 21일
0

백엔드

목록 보기
14/16

환경 세팅

  • Spring Boot + Spring Security
  • Html, CSS, JS

해결할 문제 🚩

  • CORS (Cross Origin Resource Sharing) 문제
  • 응답 헤더 (ex. Authorization 헤더) 내용이 누락되는 현상

CORS 문제에 대한 자세한 설명은 다음 글을 참고하길 바랍니다. CORS 문제 잘 정리 된 글!

문제를 인지했으면 해결책은 서버와 클라이언트가 CORS 에 대한 예외를 허용하도록 처리하면 됩니다!

몇 시간 동안 막혔던 부분은 CORS 문제는 해결했지만 응답 헤더에 Authorization 을 포함해서 전달해야하는데 클라이언트에 전달이 안 된다는 문제였습니다.

이는 cross-origin request 에게 보안상 response header 를 전부 노출시키진 않고 CORS-safelisted response headers 만 노출시키는 보안이 이유였습니다.

이를 해결하기 위해 Access-Control-Expose-Headers 를 추가 설정해줘야 했습니다! 여러분의 시간을 아껴드리기 위해 코드와 주석으로 설명을 대체하겠습니다 💕

Back 에서 처리 할 것

Spring Security Filter 에 추가할 CorsConfig 설정

@Configuration
public class CorsConfig {

//    CORS (Cross Origin Resource Sharing) 은 같은 도메인 (스키마://호스트:포트) 가 같아야 한다는 SOP(Same Origin Policy) 에 의해 실행
//    다른 도메인에서도 허락하도록 아래 설정 추가
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOriginPattern("*"); // 허용할 URL
        config.addAllowedHeader("*"); // 허용할 Header
        config.addAllowedMethod("*"); // 허용할 Http Method
        // ⭐CORS 는 해결했지만 프론트에 응답 헤더에 추가한 Authorization 이 전달되지 않는 문제 해결
        config.setExposedHeaders(Arrays.asList("Authorization", "Authorization-refresh"));
        source.registerCorsConfiguration("/**", config); // 모든 Url에 대해 설정한 CorsConfiguration 등록
        return new CorsFilter(source);
    }
}

Security Filter Chain 에 CorsConfig 필터 등록

@Slf4j
@RequiredArgsConstructor
@EnableWebSecurity
@Configuration
public class SecurityConfig {

    private final CorsConfig corsConfig;
    private final AuthenticationConfiguration authenticationConfiguration;

    @Bean
    public BCryptPasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
        http.csrf().disable();
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // disable the session
                .and()
                .formLogin().disable()
                .httpBasic().disable()
                .apply(new MyCustomDsl())
                .and()
                // ~ 권한 처리 ~
                .authorizeHttpRequests()
                .anyRequest().permitAll();
        return http.build();
    }

    //    커스텀 필터 추가를 여기서 처리하기
    public class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, HttpSecurity> {
        @Override
        public void configure(HttpSecurity http) throws Exception {
            AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
            http
//            cors 오류를 해결하기 위해 Controller 에 @CrossOrigin 을 붙여주는 방법도 있지만 
//				이 방식은 필터 추가와 다르게 인증이 필요 없는 url 만 처리해줌
                    .addFilter(corsConfig.corsFilter()) // cors 에 대해 허락하는 필터
                    .addFilter(new JwtAuthorizationFilter(authenticationManager));
        }
    }
}

Front에서 처리할 것

/* ajax 방식 */
$.ajax({
    url: "요청 URL",
    type: "요청 METHOD",
    headers: { 'Authorization': accessToken },
    xhrFields: { // CORS 문제 우회해서 헤더 넘겨주기
      withCredentials: true
    },
    success: function (data, textStatus, request) {
     
    },
    error: function (jqXHR) {
      
    }
  })
/* axios 방식 */
axios.get("https://example.com/items", {
  withCredentials: false, // default
})

/* fetch API 방식 */
fetch("https://example.com:1234/users", {
  credentials: "include",
})

withCredentials: true 옵션은 서로다른 도메인 에 요청을 보낼 때 요청에 credential 정보를 담아서 보낼 지 결정합니다. 이를 true 로 지정해야 Front 의 Authorization 헤더가 정상적으로 Back에 전달이 되겠죠!?

profile
do programming yourself

0개의 댓글