Paul Wellner Bou
Paul Wellner Bou

Reputation: 554

Is it possible to run SpringFox' swagger and swagger-ui on a different port than the main application?

We are using SpringBoot and SpringFox using @EnableSwagger2 to expose the swagger-ui.html API documentation (we don't need it to automate client code, just as documentation and test ui).

Is it possible to run all swagger related endpoints under a different port (for example the spring boot management/monitoring port) than the main application?

I researched a bit, but did not find a way in swagger's/springfox' configuration to do it. Is there a spring way to do this?

Upvotes: 2

Views: 7624

Answers (2)

Mafor
Mafor

Reputation: 10681

Yes, there is a Spring way of doing this:

Step 1. Adding an additional Tomcat connector

To add a port to the embedded server an additional connector needs to be configured. We will do it by providing custom WebServerFactoryCustomizer:

@Component
public class TomcatContainerCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

    @Value("${swagger.port}")
    private int swaggerPort;

    @Override
    public void customize(TomcatServletWebServerFactory factory) {

        Connector swaggerConnector = new Connector();
        swaggerConnector.setPort(swaggerPort);
        factory.addAdditionalTomcatConnectors(swaggerConnector);
    }
}

Now Tomcat listens on two ports but it serves the same content on both of them. We need to filter it.

Step 2. Adding a filter

Adding a servlet filter is pretty straightforward with a FilterRegistrationBean. It can be created anywhere, I added it directly to the TomcatContainerCustomizer.

@Component
public class TomcatContainerCustomizer  implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

    @Value("${swagger.port}")
    private int swaggerPort;

    @Value("${swagger.paths}")
    private List<String> swaggerPaths;

    @Override
    public void customize(TomcatServletWebServerFactory factory) {

        Connector swaggerConnector = new Connector();
        swaggerConnector.setPort(swaggerPort);
        factory.addAdditionalTomcatConnectors(swaggerConnector);
    }

    @Bean
    public FilterRegistrationBean<SwaggerFilter> swaggerFilterRegistrationBean() {

        FilterRegistrationBean<SwaggerFilter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(new SwaggerFilter());
        filterRegistrationBean.setOrder(-100);
        filterRegistrationBean.setName("SwaggerFilter");

        return filterRegistrationBean;
    }

    private class SwaggerFilter extends OncePerRequestFilter {

        private AntPathMatcher pathMatcher = new AntPathMatcher();

        @Override
        protected void doFilterInternal(HttpServletRequest httpServletRequest,
                                        HttpServletResponse httpServletResponse,
                                        FilterChain filterChain) throws ServletException, IOException {

            boolean isSwaggerPath = swaggerPaths.stream()
                    .anyMatch(path -> pathMatcher.match(path, httpServletRequest.getServletPath()));
            boolean isSwaggerPort = httpServletRequest.getLocalPort() == swaggerPort;

            if(isSwaggerPath == isSwaggerPort) {
                filterChain.doFilter(httpServletRequest, httpServletResponse);
            } else {
                httpServletResponse.sendError(404);
            }
        }
    }
}

The properties swagger.port and swagger.paths are configured in the application.yaml:

server.port: 8080
swagger:
  port: 8088
  paths: |
    /swagger-ui.html,
    /webjars/springfox-swagger-ui/**/*,
    /swagger-resources,
    /swagger-resources/**/*,
    /v2/api-docs

So far so good: the swagger-ui is served on the port 8088, our api on the 8080. But there is a problem: when we try to connect to the api from the swagger-ui, the requests are sent to the 8088 instead of 8080.

Step 3. Adjusting SpringFox config.

Swagger assumes that the api runs on the same port as the swagger-ui. We need to explicitly specify the port:

@Value("${server.port}")
private int serverPort;

@Bean
public Docket docket() {
    return new Docket(DocumentationType.SWAGGER_2)
            .host("localhost:" + serverPort);
}

And the last problem: as the ui runs on a different port than the api, the requests are considered cross-origin. We need to unblock them. It can be done globally:

@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurer() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**/*").allowedOrigins("http://localhost:" + swaggerPort);
        }
    };
}

or by adding annotations to the controllers:

@CrossOrigin(origins = "http://localhost:${swagger.port}")

Versions used: SpringBoot 2.2.2.RELEASE, springfox-swagger2 2.9.2

For a working example see https://github.com/mafor/swagger-ui-port

Upvotes: 6

Sebastian Heikamp
Sebastian Heikamp

Reputation: 976

I don't think so. When you're setting the Spring Boot management port (management.server.port), a second application server gets started to serve the actuator stuff. As far as I know there is no possibility (apart from custom actuator endpoints) to publish something on that server.
What is your use case exactly? Do you want to prevent access to Swagger in production or for non-authenticated users?

Upvotes: 0

Related Questions