I want to use swagger client generator and feed the json generated by "../v2/api-docs" from the jHipster application. The problem is that without the security definitions the generated code will not work. The JWT token is not added to the API requests, the code is generated without authentication. The http://petstore.swagger.io/v2/swagger.json example has security and securityDefinitions. Where to modify/configure the jhipster application so that the security and security definitions are generated in the json file? {I manually added the security and security definitions to the json file and after that the generated code works and JWT is enabled in the jHipster application, but I don't want to edit the file each time the API changes... } The "securityDefinitions" and "security":[{"petstore_auth":["write:pets","read:pets"]}] sections are completely missing from the generated json file from the jHipster application, even if JWT is enabled and needed to make API requests.
3 Answers
Update 28-09-2020:
Since the update to SpringFox 3, classes are now called
- SpringfoxCustomizer
- JHipsteSpringfoxCustomizer
Better late than never.
JHipster applications depend on the JHipster Framework, which is in charge of the springfox's Docket configuration.
JHipster Framework's SwaggerAutoConfiguration customizes the springfox Docket with every SwaggerCustomizer bean registered in the application. JHipster registers it's own swagger customizer for the default docket configuration.
This said, you need to add your own docket customizer ir order to include the desired security definitions and any other additional configuration to the springfox's docket. In order to do this you need to:
Create the swagger pacakage inside the already existing config package. Inside it, create a CustomSwaggerConfig class:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CustomSwaggerConfig {
public CustomSwaggerConfig() {
}
@Bean
public ApplicationSwaggerCustomizer applicationSwaggerCustomizer() {
return new ApplicationSwaggerCustomizer();
}
}
And create the ApplicationSwaggerCustomizer class:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.github.jhipster.config.apidoc.customizer.SwaggerCustomizer;
import springfox.documentation.spring.web.plugins.Docket;
public class ApplicationSwaggerCustomizer implements SwaggerCustomizer {
private final Logger log = LoggerFactory.getLogger(ApplicationSwaggerCustomizer.class);
public ApplicationSwaggerCustomizer() {
}
@Override
public void customize(Docket docket) {
log.debug("Customizing springfox docket...");
// TODO Here you can add all the configurations to the docket
}
}
Now you can add any additional docket configuration.
You can clone default implementation with:
package <YOUR_PACKAGE>;
import static io.github.jhipster.config.JHipsterConstants.SPRING_PROFILE_SWAGGER;
import static springfox.documentation.builders.PathSelectors.regex;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;
import io.github.jhipster.config.JHipsterProperties;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.web.ApiKeyVehicle;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* Springfox Swagger configuration.
* <p>
* Warning! When having a lot of REST endpoints, Springfox can become a performance issue.
* In that case, you can use the "no-swagger" Spring profile, so that this bean is ignored.
*/
@Configuration
@Profile(SPRING_PROFILE_SWAGGER)
@EnableSwagger2
public class SwaggerConfiguration {
static final String STARTING_MESSAGE = "Starting Swagger with JWT";
static final String STARTED_MESSAGE = "Started Swagger with JWT in {} ms";
static final String MANAGEMENT_TITLE_SUFFIX = "Management API";
static final String MANAGEMENT_GROUP_NAME = "management";
static final String MANAGEMENT_DESCRIPTION = "Management endpoints documentation";
public static final String AUTHORIZATION_HEADER = "Authorization";
private final Logger log = LoggerFactory.getLogger(SwaggerConfiguration.class);
private final JHipsterProperties.Swagger properties;
public SwaggerConfiguration(JHipsterProperties jHipsterProperties) {
this.properties = jHipsterProperties.getSwagger();
}
/**
* Springfox configuration for the API Swagger with JWT docs.
*
* @return the Swagger Springfox configuration
*/
@Bean
public Docket swaggerSpringfoxApiDocket() {
log.debug(STARTING_MESSAGE);
StopWatch watch = new StopWatch();
watch.start();
Docket docket = createDocket();
Contact contact = new Contact(
properties.getContactName(),
properties.getContactUrl(),
properties.getContactEmail()
);
ApiInfo apiInfo = new ApiInfo(
properties.getTitle(),
properties.getDescription(),
properties.getVersion(),
properties.getTermsOfServiceUrl(),
contact,
properties.getLicense(),
properties.getLicenseUrl(),
new ArrayList<>()
);
docket.host(properties.getHost())
.protocols(new HashSet<>(Arrays.asList(properties.getProtocols())))
.securitySchemes(Arrays.asList((apiKey())))
.securityContexts(Arrays.asList(
SecurityContext.builder()
.securityReferences(
Arrays.asList(SecurityReference.builder()
.reference("JWT")
.scopes(new AuthorizationScope[0])
.build()
)
)
.build())
)
.apiInfo(apiInfo)
.useDefaultResponseMessages(properties.isUseDefaultResponseMessages())
.forCodeGeneration(true)
.directModelSubstitute(ByteBuffer.class, String.class)
.genericModelSubstitutes(ResponseEntity.class)
.ignoredParameterTypes(Pageable.class)
.select()
.paths(regex(properties.getDefaultIncludePattern()))
.build();
watch.stop();
log.debug(STARTED_MESSAGE, watch.getTotalTimeMillis());
return docket;
}
/**
* Springfox configuration for the management endpoints (actuator) Swagger docs.
*
* @param appName the application name
* @param managementContextPath the path to access management endpoints
* @return the Swagger Springfox configuration
*/
@Bean
@ConditionalOnMissingBean(name = "swaggerSpringfoxManagementDocket")
public Docket swaggerSpringfoxManagementDocket(@Value("${spring.application.name:application}") String appName,
@Value("${management.endpoints.web.base-path}") String managementContextPath) {
ApiInfo apiInfo = new ApiInfo(
StringUtils.capitalize(appName) + " " + MANAGEMENT_TITLE_SUFFIX,
MANAGEMENT_DESCRIPTION,
properties.getVersion(),
"",
ApiInfo.DEFAULT_CONTACT,
"",
"",
new ArrayList<>()
);
return createDocket()
.apiInfo(apiInfo)
.useDefaultResponseMessages(properties.isUseDefaultResponseMessages())
.groupName(MANAGEMENT_GROUP_NAME)
.host(properties.getHost())
.protocols(new HashSet<>(Arrays.asList(properties.getProtocols())))
.securitySchemes(Arrays.asList((apiKey())))
.securityContexts(Arrays.asList(
SecurityContext.builder()
.securityReferences(
Arrays.asList(SecurityReference.builder()
.reference("JWT")
.scopes(new AuthorizationScope[0])
.build()
)
)
.build())
)
.forCodeGeneration(true)
.directModelSubstitute(ByteBuffer.class, String.class)
.genericModelSubstitutes(ResponseEntity.class)
.ignoredParameterTypes(Pageable.class)
.select()
.paths(regex(managementContextPath + ".*"))
.build();
}
protected Docket createDocket() {
return new Docket(DocumentationType.SWAGGER_2);
}
private ApiKey apiKey() {
return new ApiKey("JWT", AUTHORIZATION_HEADER, ApiKeyVehicle.HEADER.getValue());
}
} // END
At first i got a similar problem like yours and i searched to find your post. But my project uses .net core,and from the url below i found a solution. Hope it could help you if you haven't got your problem fixed. https://github.com/domaindrivendev/Swashbuckle.AspNetCore#add-security-definitions-and-requirements