1. Overview
The Swagger user interface allows us to view information about our REST services. This can be very convenient for development. However, owing to security concerns, we might not want to allow this behavior in our public environments.
In this short tutorial, we’ll look at how to turn Swagger off in production.
2. Swagger Configuration
To set up Swagger using SpringDoc, we define it in a configuration bean.
Let’s create a SwaggerConfig class:
@Configuration
public class SwaggerConfig {
@Bean
public OpenAPI openAPI() {
return new OpenAPI().info(new Info().title("SpringDoc Disable SwaggerUI example")
.description("SpringDoc Disable SwaggerUI application")
.version("v0.0.1"));
}
}
By default, this configuration bean is always injected into our Spring context. Thus, Swagger becomes available for all environments.
To disable Swagger in production, let’s toggle whether this configuration bean is injected.
3. Using Spring Profiles
In Spring, we can use the @Profile annotation to enable or disable the injection of beans.
Let’s try using a SpEL expression to match the “swagger” profile, but not the “prod” profile:
@Profile({"!prod && swagger"})
This forces us to be explicit about environments where we want to activate Swagger. It also helps to prevent accidentally turning it on in production.
We can add the annotation to our configuration:
@Configuration
@Profile({"!prod && swagger"})
public class SwaggerConfig {
...
}
Now, let’s test that it works, by launching our application with different settings for the spring.profiles.active property:
-Dspring.profiles.active=prod // Swagger is disabled
-Dspring.profiles.active=prod,anyOther // Swagger is disabled
-Dspring.profiles.active=swagger // Swagger is enabled
-Dspring.profiles.active=swagger,anyOtherNotProd // Swagger is enabled
none // Swagger is disabled
4. Using Conditionals
Spring Profiles can be too coarse-grained a solution for feature toggles. This approach can lead to configuration errors and lengthy, unmanageable lists of profiles.
As an alternative, we can use @ConditionalOnExpression, which allows specifying custom properties for enabling a bean:
@Configuration
@ConditionalOnExpression(value = "${useSwagger:false}")
public class SwaggerConfig {
...
}
If the “useSwagger” property is missing, the default here is false.
To test this, we can either set the property in the application.properties (or application.yaml) file or set it as a VM option:
-DuseSwagger=true
We should note that this example does not include any way of guaranteeing that our production instance cannot accidentally have useSwagger set to true.
5. Avoiding Pitfalls
If enabling Swagger is a security concern, then we need to choose a strategy that’s mistake-proof, but easy to use.
Some SpEL expressions can work against these aims when we use @Profile:
@Profile({"!prod"}) // Leaves Swagger enabled by default with no way to disable it in other profiles
@Profile({"swagger"}) // Allows activating Swagger in prod as well
@Profile({"!prod", "swagger"}) // Equivalent to {"!prod || swagger"} so it's worse than {"!prod"} as it provides a way to activate Swagger in prod too
This is why our @Profile example used:
@Profile({"!prod && swagger"})
This solution is probably the most rigorous, as it makes Swagger disabled by default and guarantees it cannot be enabled in “prod”.
6. Conclusion
In this article, we looked at solutions for disabling Swagger in production.
We looked at how to toggle the bean that turns Swagger on, via the @Profile and @ConditionalOnExpression annotations. We also considered how to protect against misconfiguration and undesirable defaults.
As always, the example code from this article can be found on GitHub.