Reputation: 554
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
Reputation: 10681
Yes, there is a Spring way of doing this:
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.
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.
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
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