Integración de Swagger2 con Spring Boot 2.6+ (Solución probada y funcional)

Integración de Spring Boot 2.6+ con Swagger2

  1. Integración con versiones anteriores a Spring Boot 2.6

1-1: Configuración en la clase principal de Spring Boot Esta configuración permite ver directamente las rutas al iniciar el proyecto, facilitendo pruebas directas:

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;

import java.net.InetAddress;
import java.net.UnknownHostException;

@Slf4j
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) throws UnknownHostException {
        ConfigurableApplicationContext appContext = SpringApplication.run(DemoApplication.class, args);
        Environment environment = appContext.getEnvironment();
        String localIp = InetAddress.getLocalHost().getHostAddress();
        String puerto = environment.getProperty("server.port");
        String contexto = environment.getProperty("server.servlet.context-path");
        
        log.info("\n----------------------------------------------------------\n\t" +
                "Aplicación Demo está corriendo! URLs de acceso:\n\t" +
                "Local: \t\thttp://localhost:" + puerto + contexto + "/\n\t" +
                "Externo: \thttp://" + localIp + ":" + puerto + contexto + "/\n\t" +
                "Interfaz swagger: \thttp://" + localIp + ":" + puerto + contexto + "/swagger-ui.html\n\t" +
                "Documentación: \t\thttp://" + localIp + ":" + puerto + contexto + "/doc.html\n" +
                "----------------------------------------------------------");
    }
}

1-2: Clase de configuración de Swagger

package com.demo.config;

import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.ArrayList;
import java.util.List;

@Configuration
@EnableSwagger2
public class DocumentacionConfig {

    @Bean
    public Docket construirApiRest() {
        ParameterBuilder parametroToken = new ParameterBuilder();
        List<Parameter> parametros = new ArrayList<>();
        parametroToken.name("Authorization-Token").description("Token de autenticación").modelRef(new ModelRef("string")).parameterType("header").required(false).build();
        parametros.add(parametroToken.build());
        
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(informacionApi())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.demo.controlador"))
                .apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
                .paths(PathSelectors.any())
                .build()
                .globalOperationParameters(parametros);
    }

    private ApiInfo informacionApi() {
        return new ApiInfoBuilder()
                .title("API Documentación Servicio Backend Demo")
                .description("Estilo de interfaz RESTful")
                .termsOfServiceUrl("http://www.ejemplo.com/")
                .version("1.0")
                .build();
    }
}

1-3: Configuración en archivo YAML o properties

server:
  port: 8080
  servlet:
    context-path: /demoproject

spring:
  mvc:
    pathmatch:
      matching-strategy: ANT_PATH_MATCHER
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/basededatos?characterEncoding=utf8&useSSL=false&autoReconnect=true
    username: usuario
    password: contraseña
  
mybatis:
  type-aliases-package: com.demo.entidad
  mapper-locations: classpath:mapper/*.xml

spring:
  web:
    resources:
      static-locations: classpath:/templates/,classpath:/META-INF/resources/,classpath:/resources/
  thymeleaf:
    cache: false

1-4: Configuración del archivo POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
    <modelVersion>4.0.0</modelVersion>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.1</version>
    </parent>
    
    <groupId>com.demo</groupId>
    <artifactId>proyectodemo</artifactId>
    <version>1.0.0</version>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        
        <!-- Dependencias de Swagger -->
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
            <version>1.5.21</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>swagger-bootstrap-ui</artifactId>
            <version>1.9.3</version>
        </dependency>
        
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
</project>

1-5: Protección de doucmentación con autenticación Para proteger la documentación de accesos no autorizados, se puede implementar autenticación básica.

1-6: Activación de interfaz mejorada Agregar la dependencia ya mencionada para habilitar la interfaz bootstrap.

1-7: Actualización de la clase de configuración

@Configuration
@EnableSwagger2
@EnableSwaggerBootstrapUI
public class DocumentacionConfig {
    // Configuración existente...
}

1-8: Configuración de credenciales en YAML

swagger:
  basic:
    enable: true
    username: administrador
    password: contraseñasegura

1-9: Anotaciones comunes de Swagger

En clases controladoras:

@Api(tags = "Gestión de Usuarios")

En métodos de controladores:

@ApiOperation(value = "Obtener usuario por ID", notes = "Recupera un usuario específico")

En parámetros de métodos:

@ApiParam(value = "Identificador único", required = true)

Ejemplo cmopleto:

@ApiOperation(value = "Buscar por identificador")
@GetMapping(value = {"/buscar/{identificador}"})
public ResultadoBusqueda encontrarPorId(
    @ApiParam(value = "ID principal", required = true) 
    @PathVariable String identificador) {
    EntidadDatos entidad = servicio.obtenerPorId(identificador);
    return ResultadoBusqueda.construir(ResultadoBusqueda.EXITO, "Consulta exitosa", entidad);
}

Parámetros implícitos:

@ApiImplicitParams({
    @ApiImplicitParam(name="telefono",value="Número de contacto",required=true,paramType="query",dataType="Long"),
    @ApiImplicitParam(name="clave",value="Contraseña",required=true,paramType="query",dataType="String")
})

Respuestas documentadas:

@ApiResponses({
    @ApiResponse(code = 200, message = "Operación exitosa"),
    @ApiResponse(code = 401, message = "No autorizado"),
    @ApiResponse(code = 404, message = "Recurso no encontrado")
})

En clases modelo:

@ApiModel(value="UsuarioModelo", description="Representación de usuario")

En propiedades de modelos:

@ApiModelProperty(value = "Estado" ,required = true, example = "activo")

  1. Integración con Spring Boot 2.6+

2-1: Configuración actualizada del POM

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.6.6</version>
</parent>

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <version>3.0.3</version>
</dependency>

2-2: Clase de configuración actualizada

@Configuration
@EnableSwagger2
@Import(BeanValidatorPluginsConfiguration.class)
public class ConfiguracionDocumentacion implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry gestor) {
        gestor.addResourceHandler("swagger-ui.html")
              .addResourceLocations("classpath:/META-INF/resources/");
        gestor.addResourceHandler("doc.html")
              .addResourceLocations("classpath:/META-INF/resources/");
        gestor.addResourceHandler("/webjars/**")
              .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

    @Bean(value = "apiPorDefecto")
    public Docket apiPorDefecto() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(infoApi())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.demo.controladores"))
                .apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
                .paths(PathSelectors.any())
                .build()
                .securitySchemes(Collections.singletonList(esquemaSeguridad()))
                .securityContexts(contextosSeguridad())
                .globalOperationParameters(configurarTokenEncabezado());
    }

    @Bean
    SecurityScheme esquemaSeguridad() {
        return new ApiKey("Authorization-Token", "Authorization-Token", "header");
    }

    private List<Parameter> configurarTokenEncabezado() {
        ParameterBuilder builder = new ParameterBuilder();
        List<Parameter> lista = new ArrayList<>();
        builder.name("Authorization-Token")
               .description("Token de autorización")
               .modelRef(new ModelRef("string"))
               .parameterType("header")
               .required(false)
               .build();
        lista.add(builder.build());
        return lista;
    }

    private ApiInfo infoApi() {
        return new ApiInfoBuilder()
                .title("Documentación API Demo Backend")
                .version("1.0")
                .description("Interfaz API Backend")
                .contact(new Contact("Organización", "https://ejemplo.com", "contacto@ejemplo.com"))
                .license("Licencia Apache 2.0")
                .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
                .build();
    }

    private List<SecurityContext> contextosSeguridad() {
        return new ArrayList(
                Collections.singleton(SecurityContext.builder()
                        .securityReferences(autenticacionPorDefecto())
                        .forPaths(PathSelectors.regex("^(?!auth).*$"))
                        .build())
        );
    }

    private List<SecurityReference> autenticacionPorDefecto() {
        AuthorizationScope scope = new AuthorizationScope("global", "accesoCompleto");
        AuthorizationScope[] scopes = new AuthorizationScope[1];
        scopes[0] = scope;
        return new ArrayList(
                Collections.singleton(new SecurityReference("Authorization-Token", scopes)));
    }
}

2-3: Solución de compatibilidad de patrones de ruta En Spring Boot 2.6+, agregar esta configuración en application.yml:

spring:
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher

2-4: Configuración de autenticación para knife4j

knife4j:
  enable: true
  production: false
  basic:
    enable: true
    username: admin
    password: contraseña123

  1. Consideración importante sobre Spring Security Cuando el proyecto incluye Spring Security junto con autenticación de Swagger, pueden surgir conflictos que causen fallos de autenticación. En este caso, se debe ajustar la configuración de seguridad para permitir el acceso a los recursos de Swagger.
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
</dependency>

Etiquetas: spring-boot Swagger knife4j springfox api-documentation

Publicado el 8-2 02:34