본문 바로가기
Programming/Spring

ResponseBodyAdvice - Swagger 2 with Security 해결!!!!

by dinB 2022. 4. 29.

https://stackoverflow.com/questions/47425048/why-does-springfox-swagger2-ui-tell-me-unable-to-infer-base-url

 

Why does springfox-swagger2 UI tell me "Unable to infer base url."

Why does springfox-swagger2 UI tell me Unable to infer base url. As far as I know, I am using a typical Swagger spring-boot configuration. As you can see in the screenshot, the swagger-fox url

stackoverflow.com

 

스프링 시큐리티를 사용하는 환경에서 스웨거를 붙여서 쓰려고 하니 스웨거의 경로로 접근이 안 되는 문제가 발생했다.

여러 삽질을 모두 다 해보았지만 해결이 되지 않았었는데, 나름 야매(?) 적인 방식인지는 모르겠지만 해결 방법을 찾아 포스팅한다.

 

나의 경우는 RestControllerAdvisor 를 활용하여 공통 Response DTO 를 만들어서 클라이언트 단에 반환하고 있었다.

 

즉, Advisor 를 사용하고 있다면 beforeBodyWrite 에게 Response 를 넘길지 말지를 판단하는 supports 메소드 단에서 Swagger 엔드 포인트에 대하여 무시 처리를 해주어야 한다.

 

즉, Swagger를 관리하는 Swagger2Controller와 ApiResourceController 가 Custom Response(beforeBodyWrite 메소드)를 통과하지 못하도록 막아야 한다.

 

package egovframework.lms.boot.advisor;

import egovframework.lms.boot.dto.CommonResponseDto;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.io.IOException;

@RestControllerAdvice
public class RestResponseAdvisor<T> implements ResponseBodyAdvice<T> {

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        String className = returnType.getContainingClass().getSimpleName();
        // Swagger2Controller 와 ApiResourceController 에 대한 요청일 경우 return false 로 처리한다.
        if(className.equals("ExceptionHandler") || className.equals("Swagger2Controller") || className.equals("ApiResourceController"))
            return false;
        return true;
    }

    @Override
    public T beforeBodyWrite(T body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        // 공통 Response 생성
        ServletServerHttpResponse httpResponse = (ServletServerHttpResponse) response;
        Integer statusCode = httpResponse.getServletResponse().getStatus();

        CommonResponseDto responseDto = CommonResponseDto.builder()
                .success(true)
                .statusCode(statusCode)
                .msg("")
                .data(body)
                .build();

        return (T) responseDto;
    }
}

 

아 또, custom context path 를 사용하고 있다면 (나의 경우는 /api/v1) Swagger Config 에서 Path Mapping을 /로 지정해주어야 엔드포인트가 정상 호출된다. 

 

만일, Path Mapping 을 Custom Context Path 에 따라 /api/v1 로 지정한다면 호출 시 /api/v1/api/v1 과 같이 Context Path 가 두 번 붙는 경우가 발생한다.

 

package egovframework.lms.boot.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket docket(){
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(
                        RequestHandlerSelectors.any())
                .paths(PathSelectors.any())
                .build()
                // Before
                // .pathMapping("/api/v1")
                // After
                .pathMapping("/")
                .apiInfo(
                        new ApiInfoBuilder()
                                .title("Spring Security take over Swagger Authentication authorization")
                                .description("Spring Security and Swagger")
                                .version("1.0.0")
                                .contact(
                                        new Contact(
                                                "Fox under the tree",
                                                "https://www.ramostear.com",
                                                "ramostear@163.com"
                                        )
                                ).build()
                );
    }

}

 

정상 동작한다.