포스트

Spring Boot - SpringDoc (Swagger) 설정

application.yml

1
2
3
4
5
springdoc:  
  show-login-endpoint: true  
  swagger-ui:  
    tags-sorter: alpha  
    groups-order: asc
  • springdoc.show-login-endpoint
    • security login 사용시에 엔드포인트 노출 설정 (기본값: false)

  • springdoc.swagger-ui.tags-sorter: tag 정렬 방법 설정
    • 또는 springdoc.swagger-ui.tagsSorter
    • 설정 값: alpha, 사용자 정의
    • 사용자 정의 정렬 구현 방법: yml에서 해당 옵션 설정하지 않고 java로 커스텀 정렬 구현 후 bean 등록
      • java 작성 예시
        1
        2
        3
        4
        5
        6
        7
        
          @Bean
          public OpenApiCustomiser sortTagsAlphabetically() {
              return openApi -> openApi.setTags(openApi.getTags()
                      .stream()
                      .sorted(Comparator.comparing(tag -> StringUtils.stripAccents(tag.getName())))
                      .collect(Collectors.toList()));
          }
        



  • springdoc.swagger-ui.groups-order: group 정렬 방법 설정
    • 또는 springdoc.swagger-ui.operationsSorter
    • 설정 값: alpha, asc, desc, method
      • method로 설정하면 http method (GET, POST, DELETE 등)으로 정렬


java

기본 설정

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Configuration  
public class SwaggerConfig {  
    @Bean  
    public OpenAPI openAPI() {  
        Info info = new Info()  
                .version("1.0")  
                .title("Simple Board 02 - User Service")  
                .description("""    
📢 인증키 입력시 다음 형태로 입력해야 한다.  
 Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJnaW13bGd1c0BnbWFpbC5jb20iLCJpZCI6MywiZX
        """);  

        // server url setting
        Server server = new Server();  
        server.setUrl("http://localhost:8000/api/user"); // 이 주소에 end point 붙여 실행
    
        // Authorize - jwt setting
        String jwtKey = "x-token";  
        SecurityRequirement securityRequirement = new SecurityRequirement().addList(jwtKey);  
        Components components = new Components()  
                .addSecuritySchemes(jwtKey, new SecurityScheme()  
                        .name(jwtKey)  
                        .type(SecurityScheme.Type.APIKEY) // APIKEY, HTTP, OAUTH2, OPENIDCONNECT, MUTUALTLS
                        .scheme("bearer")  
                        .bearerFormat("JWT") // JWT, OAuth 등  
                        .in(SecurityScheme.In.HEADER)); // COOKIE, HEADER, QUERY
    
        return new OpenAPI()  
                .info(info)  
                .components(components)  
                .addSecurityItem(securityRequirement)
                .servers(List.of(server));  
    }
}


그룹 설정 - 기본

image

1. 특정 end-point를 하나의 그룹으로 묶음

1
2
3
4
5
6
7
8
9
10
@Configuration  
public class SwaggerConfig {  
    @Bean  
    public GroupedOpenApi signUpGroupedOpenApi() {  
        return GroupedOpenApi.builder()  
                .group("그룹 이름")  
                .pathsToMatch("/end/point/1", "/end-point/**")  
                .build();  
    }  
}

2. 특정 end-point를 제외하고 하나의 그룹으로 묶음

1
2
3
4
5
6
7
8
9
10
@Configuration  
public class SwaggerConfig {  
    @Bean  
    public GroupedOpenApi signUpGroupedOpenApi() {  
        return GroupedOpenApi.builder()  
                .group("그룹 이름")  
                .pathsToExclude("/end/point/1", "/end-point/**")  
                .build();  
    }  
}


그룹 설정 - security login 사용하는 경우

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
@Configuration  
public class SwaggerConfig {  
    @Bean  
    public GroupedOpenApi memberGroupedOpenApi() {  
        return GroupedOpenApi.builder()  
            .group("2. 회원 API")  
            .pathsToExclude("/end/point/1", "/end-point/**")  
            .addOpenApiCustomizer(securityLoginEndpointCustomiser()) // 추가
            .build();  
        }  
    }

    // 커스텀 시큐리티 로그인 정보 반영
    public OpenApiCustomizer securityLoginEndpointCustomiser() {  
        FilterChainProxy filterChainProxy = applicationContext.getBean(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME, FilterChainProxy.class);  
        return openAPI -> {  
            for (SecurityFilterChain filterChain : filterChainProxy.getFilterChains()) {  
                Optional<AuthenticationFilter> optionalFilter =  
                        filterChain.getFilters().stream()  
                                .filter(AuthenticationFilter.class::isInstance)
                                .map(AuthenticationFilter.class::cast)  
                                .findAny();  
                if (optionalFilter.isPresent()) {  
                    AuthenticationFilter authenticationFilter = optionalFilter.get();  
                    Operation operation = new Operation();  
                    Schema<?> schema = new ObjectSchema()  
                            .addProperty("email", new StringSchema())  
                            .addProperty("password", new StringSchema());  
  
                    String mediaType = org.springframework.http.MediaType.APPLICATION_JSON_VALUE;  
                    RequestBody requestBody = new RequestBody().required(true).content(new Content().addMediaType(mediaType, new MediaType().schema(schema)));  
                    operation.requestBody(requestBody);  
  
                    ApiResponses apiResponses = new ApiResponses();  
                    apiResponses.addApiResponse(String.valueOf(HttpStatus.OK.value()),new ApiResponse().description(HttpStatus.OK.getReasonPhrase()));  
                    apiResponses.addApiResponse(String.valueOf(HttpStatus.FORBIDDEN.value()), new ApiResponse().description(HttpStatus.FORBIDDEN.getReasonPhrase()));  
                    operation.responses(apiResponses);  
  
                    operation.addTagsItem("1. 로그인");  
                    operation.description("로그인 후 인증키는 header에서 `x-token` 확인");  
  
                    PathItem pathItem = new PathItem().post(operation);  
                    try {  
                        Field requestMatcherField = AbstractAuthenticationProcessingFilter.class.getDeclaredField("requiresAuthenticationRequestMatcher");  
                        requestMatcherField.setAccessible(true);  
                        AntPathRequestMatcher requestMatcher = (AntPathRequestMatcher) requestMatcherField.get(authenticationFilter);  
                        String loginPath = requestMatcher.getPattern();  
                        requestMatcherField.setAccessible(false);  
                        openAPI.getPaths().addPathItem(loginPath, pathItem);  
                    }  
                    catch (NoSuchFieldException | IllegalAccessException |  
                           ClassCastException ignored) {  
                        // Exception escaped  
                        log.trace(ignored.getMessage());  
                    }  
                }  
            }  
        };  
    }
}
  • securityLoginEndpointCustomiser() 메서드는 사용자 정의 security login 정보를 반영하기 위해 org.springdoc.security.SpringDocSecurityConfiguration.SpringDocSecurityConfiguration#springSecurityLoginEndpointCustomiser 코드를 가져와서 수정






참고한 사이트

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.