공부내용공유

Spring security CorsConfigurer (feat: spring mvc WebMvcConfiguer) 본문

Spring/Spring Security

Spring security CorsConfigurer (feat: spring mvc WebMvcConfiguer)

forfun 2024. 2. 4. 18:52

서론


cors 에 관한 설정은 security를 사용한다면 bean 이름을 security에서 제공하는 bean 이름으로 만들면 filter에 자동으로 주입이 된다.

 

그리고 spring mvc 차원에서도 WebMvcConfiguer와 같은 클래스를 상속하고 메서드를 오버라이딩 하면 해당 메서드에서 커스터마이징 한대로 설정이 적용된다.

 

만약 2가지 설정이 있으면 어떤 것이 작동하는지 궁금해져서 security 내부 코드를 간단하게 살펴보고 정리하였다.

 

 

본론


CorsConfigurer class

public CorsConfigurer() {}

public CorsConfigurer<H> configurationSource(CorsConfigurationSource configurationSource) {
    this.configurationSource = configurationSource;
    return this;
}

 

위 코드는 CorsConfigurer 클래스의 생성자들로 2개가 있는데 저 기본 생성자는 HttpSecurity.cors() 를 사용하면 호출된다고 나와있다.

 

(아래 CorsConfigurationSourece 를 주입받는 생성자는 아직 잘 모르겠다. 추후 알게되면 내용을 추가할 예정이다.)

 

getCorsFilter method

private CorsFilter getCorsFilter(ApplicationContext context) {
        if (this.configurationSource != null) {
            return new CorsFilter(this.configurationSource);
        }
        boolean containsCorsFilter = context.containsBeanDefinition(CORS_FILTER_BEAN_NAME);
        if (containsCorsFilter) {
            return context.getBean(CORS_FILTER_BEAN_NAME, CorsFilter.class);
        }
        boolean containsCorsSource = context.containsBean(CORS_CONFIGURATION_SOURCE_BEAN_NAME);
        if (containsCorsSource) {
            CorsConfigurationSource configurationSource = context.getBean(CORS_CONFIGURATION_SOURCE_BEAN_NAME,
                    CorsConfigurationSource.class);
            return new CorsFilter(configurationSource);
        }
        boolean mvcPresent = ClassUtils.isPresent(HANDLER_MAPPING_INTROSPECTOR, context.getClassLoader());
        if (mvcPresent) {
            return MvcCorsFilter.getMvcCorsFilter(context);
        }
        return null;
	}

위 코드는 CorsConfigurer 클래스에 있는 getCorsFilter라는 메소드이다.

 

맨처음 로직에서 configurationSource 가 있으면 바로 CorsFilter에 주입해서 반호나을 해주는데 여기서는 기본생성자로 class를 생성해 configurationSource 가 null일 때를 기준으로 로직을 설명하겠다.

 

CorsFilter

제일 먼저 CORS_FILTER_BEAN_NAME 라는 bean을 찾는다.

해당 상수는 아래와 같은 형태로 class에 선언되어져 있다.

private static final String *CORS_FILTER_BEAN_NAME* = "corsFilter"; 

corsFilter라는 이름의 bean이 있다면 바로 return 해준다.

 

CorsConfigurationSource

 

만약 CORS_FILTER_BEAN_NAME 도 없다면 다음으로는

 

CORS_CONFIGURATION_SOURCE_BEAN_NAME 라는 bean을 찾는 것을 볼 수 있는데

private static final String *CORS_CONFIGURATION_SOURCE_BEAN_NAME =*"corsConfigurationSource";

 

이 또한 이렇게 상수로 정의 되어져 있다.

 

이렇게 config class를 찾아서 CorsFilter에 주입하여 corsFilter를 return 한다.

 

 

MVC CorsConfig

 

여기서 corsConfigurationsSource Bean도 없다면 

 

HANDLER_MAPPING_INTROSPECTOR를 찾는 것을 볼 수 있는데

private static final String HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector";

 

해당 상수의 실제 값이다.

 

실제로 mvcCorsBean을 가져올 때는

if (mvcPresent) {
	return MvcCorsFilter.getMvcCorsFilter(context);
}

이러한 로직으로 가져옴을 볼 수 있는데

 

해당 메소드를 따라 들어가면

private static CorsFilter getMvcCorsFilter(ApplicationContext context) {
    if (!context.containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
        throw new NoSuchBeanDefinitionException(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, "A Bean named "
                + HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME + " of type "
                + HandlerMappingIntrospector.class.getName()
                + " is required to use MvcRequestMatcher. Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext.");
    }
    HandlerMappingIntrospector mappingIntrospector = context.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME,
            HandlerMappingIntrospector.class);
    return new CorsFilter(mappingIntrospector);
}

이러한 코드가 나온다.

 

위 메소드에서 사용된 HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME 의 실제 값을 보면

private static final String HANDLER_MAPPING_INTROSPECTOR 
= "org.springframework.web.servlet.handler.HandlerMappingIntrospector";

이러한 상수이다.

 

 

여기서 return 해주는 HandlerMappingIntrospector 클래스는 당연하게도 CorsConfigurationSource 인터페이스를 구현하고 있고 corsFilter class에서 사용되는 getCorsConfiguration 메서드도 구현하고 있다. 

@Override
@Nullable
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
    AttributesPreservingRequest wrappedRequest = new AttributesPreservingRequest(request);
    return doWithHandlerMappingIgnoringException(wrappedRequest, (handlerMapping, executionChain) -> {
        for (HandlerInterceptor interceptor : executionChain.getInterceptorList()) {
            if (interceptor instanceof CorsConfigurationSource) {
                return ((CorsConfigurationSource) interceptor).getCorsConfiguration(wrappedRequest);
            }
        }
        if (executionChain.getHandler() instanceof CorsConfigurationSource) {
            return ((CorsConfigurationSource) executionChain.getHandler()).getCorsConfiguration(wrappedRequest);
        }
        return null;
    });
}

해당 메서드에서 interceptor list를 조회하면서 corsConfig가 있을 경우 return 해 줌을 볼 수 있다.

 

결론


이번 글은 간단하게 security에서 지원하는 bean들을 먼저 찾고 없다면 mvc에서 지원해주는 cors config를 사용함을 확인하였다.

 

다음 글에서는 mvc에서 어떤 식으로 cors config를 관리하고 설정 할 수 있는지 좀 더 자세히 코드를 뜯어보고 , cors 처리를 어떤 식으로도 해주는지 살펴볼 예정이다!