Passer au contenu

Mettre en place une spécification OpenAPI avec Spring Boot

Installer, configurer et créer une spécification OpenAPI avec un exemple

Le code de ce tutoriel est disponible sur GitHub, si vous souhaitez le consulter directement.
https://github.com/Kwaadpepper/Demo-SpringBoot-OpenApi

Spring%20Initializer.png

Initialiser un projet

Nous utiliserons Spring Web, Spring Security pour la configuration en cas réel et OpenAPI avec l'interface Swagger intégrée directement qui est optionnelle.
Nous réaliserons quelques routes d'exemple et ajouterons des annotations.
Vous pouvez utiliser https://start.spring.io/ pour initialiser votre projet, j'utiliserai Java 21 et Spring Boot 3.5.5.

Voici ce que vous devriez avoir dans vos dépendances :

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.5.5'
	id 'io.spring.dependency-management' version '1.1.7'
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-security'
	implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-actuator'

	implementation 'org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.11'
	implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.11'

	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.springframework.security:spring-security-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

Notez que j'ai ajouté les dépendances de springdoc pour publier la spécification OpenAPI ainsi que pour avoir l'interface Swagger UI qui est cependant optionnelle.

Configurer OpenAPI

Vous allez avoir besoin d'un Bean pour la configuration de la spécification.

@Configuration
public class OpenApiConfig {

  @Bean
  OpenAPI openApi() {
    return new OpenAPI()
        .addSecurityItem(new SecurityRequirement().addList("Cookie Authentication"))
        .components(
            new Components().addSecuritySchemes("Cookie Authentication", createApiCookieScheme()))
        .info(
            new Info()
                .title("Demo OpenApi")
                .description("Demo OpenApi using Spring Boot and Spring Security")
                .version("0.0.1")
                .contact(
                    new Contact()
                        .name("Munsch Jeremy")
                        .email("github@jeremydev.ovh")
                        .url("https://jeremydev.ovh")));
  }

  private SecurityScheme createApiCookieScheme() {
    return new SecurityScheme()
        .type(SecurityScheme.Type.APIKEY)
        .in(SecurityScheme.In.COOKIE)
        .name(CookieService.COOKIE_NAME);
  }
}

Et voici un exemple de configuration pour Spring Security :

@Configuration
public class SpringSecurityConfig {

  @Bean
  SecurityFilterChain securityFilterChain(
      HttpSecurity http, StaticCredentialFilter staticCredentialFilter) throws Exception {

    final List routesToIgnore =
        List.of(
            "/api/auth/login",
            "/v3/api-docs/**",
            "/swagger-ui.html",
            "/swagger-ui/**",
            "/actuator/**");

    return http.sessionManagement(
            // No cookie session, just stateless API.
            session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
        // No CSRF for stateless APIs.
        .csrf(AbstractHttpConfigurer::disable)
        // 401 on unauthenticated requests.
        .exceptionHandling(
            handling ->
                handling.authenticationEntryPoint(
                    new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)))
        // All requests must be authenticated unless explicitly ignored.
        .authorizeHttpRequests(
            request -> {
              // Non-protected URLs:
              request.requestMatchers(routesToIgnore.toArray(String[]::new)).permitAll();

              // Any other routes are protected.
              request.anyRequest().fullyAuthenticated();
            })
        // Custom filter to process our static user authentication.
        .addFilterAt(staticCredentialFilter, UsernamePasswordAuthenticationFilter.class)
        .build();
  }
}

Notez que dans le Bean pour OpenAPI, il est spécifié une authentification par Cookie.
Ainsi, nous disons que les routes sont protégées par défaut, ce qui est le cas dans notre configuration Spring Security.
Cependant, pour définir une route non protégée comme celle de login, nous pouvons utiliser les annotations suivantes :

  @io.swagger.v3.oas.annotations.parameters.RequestBody(
      content =
          @Content(
              mediaType = "application/json",
              schema = @Schema(implementation = LoginRequest.class),
              examples = {
                @ExampleObject(
                    name = "Exemple de login",
                    value = "{\"login\": \"user.example\", \"password\": \"Password.1\"}")
              }))
  @ApiResponses(
      value = {
        @ApiResponse(
            responseCode = "200",
            description = "Successfully authenticated",
            headers = {
              @Header(
                  name = "set-cookie",
                  description = "Session cookie HTTP Only",
                  schema = @Schema(type = "string"))
            },
            content = @Content(schema = @Schema(implementation = ResponseDto.class))),
        @ApiResponse(
            responseCode = "401",
            description = "User could not be authenticated",
            content =
                @Content(
                    mediaType = "application/json",
                    schema = @Schema(implementation = ApiErrorDetails.class)))
      })
  @SecurityRequirements
  @PostMapping(
      value = "/api/auth/login",
      consumes = MediaType.APPLICATION_JSON_VALUE,
      produces = MediaType.APPLICATION_JSON_VALUE)
  public ResponseEntity login(@Valid @RequestBody final LoginRequest request) {
      ....
  }

Notez @SecurityRequirements : cette annotation est définie comme étant vide ; ainsi, nous indiquons que la route n'est pas protégée.
Pour avoir une spécification complète, il convient d'ajouter des annotations sur les requêtes et les réponses.

Il peut être intéressant de générer la spécification et de s'y conformer à l'aide d'un client généré comme HeyApi à l'instar de SOAP. L'autre approche consistant à créer d'abord la spécification manuellement puis à adapter le backend peut s'avérer plus exigeante sans validation programmatique stricte.

Consultez la documentation officielle concernant les annotations Swagger. Il existe également des bibliothèques pour générer des clients d'API à partir de la spécification.

Accéder à la spécification OpenAPI

Une fois l'application démarrée :

Accédez au document brut OpenAPI sur http://localhost:8080/v3/api-docs et à l'interface Swagger UI sur http://localhost:8080/swagger-ui/index.html

SwaggerUI.png
juniko
4 min