当我点击执行时, swagger 地无限加载

snz8szmq  于 2022-11-06  发布在  其他
关注(0)|答案(1)|浏览(576)

我正在使用Swagger与keycloak。我得到了下面的错误,当我点击执行按钮。
当我在swagger上点击Execute时,我看到了正在加载的图像,并且在网络抽头上没有任何请求,因为我在控制台上有一个错误。
我将添加我的配置代码和YML文件,让你看到我做什么。
谁能帮帮我,好吗?
以下是控制台中的输出:

以下是控制台点击中的错误代码(其生成的代码):

下面是swagger用户界面页面:

下面是swagger配置:

@Slf4j
@Configuration
@TbCoreComponent
public class SwaggerConfiguration {

    @Value("${swagger.api_path_regex}")
    private String apiPathRegex;
    @Value("${swagger.security_path_regex}")
    private String securityPathRegex;
    @Value("${swagger.non_security_path_regex}")
    private String nonSecurityPathRegex;
    @Value("${swagger.title}")
    private String title;
    @Value("${swagger.description}")
    private String description;
    @Value("${swagger.contact.name}")
    private String contactName;
    @Value("${swagger.contact.url}")
    private String contactUrl;
    @Value("${swagger.contact.email}")
    private String contactEmail;
    @Value("${swagger.version}")
    private String version;
    @Value("${app.version:unknown}")
    private String appVersion;

    // Used to get token from Keyclaok
    @Value("${swagger.auth.token_url}")
    private String keycloakAuthTokenUrl;
    @Value("${security.keycloak.realm}")
    private String keycloakRealm;
    @Value("${security.keycloak.clientId}")
    private String keyclaokAuthCliendId;

    @Bean
    public Docket yousefApi() {
        TypeResolver typeResolver = new TypeResolver();
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("Hammad")
                .apiInfo(apiInfo())
                .additionalModels(
                        typeResolver.resolve(ThingsboardErrorResponse.class),
                        typeResolver.resolve(ThingsboardCredentialsExpiredResponse.class)
                )
                .select()
                .paths(apiPaths())
                .paths(any())
                .build()
                .globalResponses(HttpMethod.GET, defaultErrorResponses(false))
                .globalResponses(HttpMethod.POST, defaultErrorResponses(true))
                .globalResponses(HttpMethod.DELETE, defaultErrorResponses(false))
                .securitySchemes(newArrayList(apiKey()))
                .securityContexts(newArrayList(securityContext()))
                .enableUrlTemplating(true);
    }

    @Bean
    @Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER)
    ApiListingBuilderPlugin loginEndpointListingBuilder() {
        return new ApiListingBuilderPlugin() {
            @Override
            public void apply(ApiListingContext apiListingContext) {
                if (apiListingContext.getResourceGroup().getGroupName().equals("Hammad")) {
                    ApiListing apiListing = apiListingContext.apiListingBuilder().build();
                    if (apiListing.getResourcePath().equals(keycloakAuthTokenUrl)) {
                        apiListingContext.apiListingBuilder().tags(Set.of(new Tag("login-endpoint", "Login Endpoint")));
                        apiListingContext.apiListingBuilder().description("Login Endpoint");
                    }
                }
            }

            @Override
            public boolean supports(@NotNull DocumentationType delimiter) {
                return DocumentationType.SWAGGER_2.equals(delimiter) || DocumentationType.OAS_30.equals(delimiter);
            }
        };
    }

    @Bean
    UiConfiguration uiConfig() {
        return UiConfigurationBuilder.builder()
                .deepLinking(true)
                .displayOperationId(false)
                .defaultModelsExpandDepth(1)
                .defaultModelExpandDepth(1)
                .defaultModelRendering(ModelRendering.EXAMPLE)
                .displayRequestDuration(false)
                .docExpansion(DocExpansion.NONE)
                .filter(false)
                .maxDisplayedTags(null)
                .operationsSorter(OperationsSorter.ALPHA)
                .showExtensions(false)
                .showCommonExtensions(false)
                .supportedSubmitMethods(UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS)
                .validatorUrl(null)
                .persistAuthorization(true)
                .syntaxHighlightActivate(true)
                .syntaxHighlightTheme("agate")
                .build();
    }

    private ApiKey apiKey() {
        return new ApiKey("Bearer", "X-Authorization", "header");
    }

    private OAuth securityScheme() {
        List<GrantType> grantTypes = newArrayList(new ResourceOwnerPasswordCredentialsGrant(keycloakAuthTokenUrl));
        return new OAuth("KeycloakAuth", new ArrayList<>(), grantTypes);
    }

    private SecurityContext securityContext() {
        return SecurityContext.builder()
                .securityReferences(defaultAuth())
                .operationSelector(securityPathOperationSelector())
                .build();
    }

    private Predicate<String> apiPaths() {
        return regex(apiPathRegex);
    }

    private Predicate<OperationContext> securityPathOperationSelector() {
        return new SecurityPathOperationSelector(securityPathRegex, nonSecurityPathRegex);
    }

    private AuthorizationScope[] scopes() {
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[3];
        authorizationScopes[0] = new AuthorizationScope(Authority.SYS_ADMIN.name(), "System administrator");
        authorizationScopes[1] = new AuthorizationScope(Authority.TENANT_ADMIN.name(), "Tenant administrator");
        authorizationScopes[2] = new AuthorizationScope(Authority.CUSTOMER_USER.name(), "Customer");
        return authorizationScopes;
    }

    List<SecurityReference> defaultAuth() {
        return newArrayList(new SecurityReference("KeycloakAuth", scopes()), new SecurityReference("Bearer", scopes()));
    }

    private ApiInfo apiInfo() {
        String apiVersion = version;
        if (StringUtils.isEmpty(apiVersion)) {
            apiVersion = appVersion;
        }
        return new ApiInfoBuilder()
                .title(title)
                .description(description)
                .contact(new Contact(contactName, contactUrl, contactEmail))
                .version(apiVersion)
                .build();
    }

    /**Helper methods**/

    private List<Response> defaultErrorResponses(boolean isPost) {
        return List.of(
                errorResponse("400", "Bad Request",
                        ThingsboardErrorResponse.of(isPost ? "Invalid request body" : "Invalid UUID string: 123", ThingsboardErrorCode.BAD_REQUEST_PARAMS, HttpStatus.BAD_REQUEST)),
                errorResponse("401", "Unauthorized",
                        ThingsboardErrorResponse.of("Authentication failed", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)),
                errorResponse("403", "Forbidden",
                        ThingsboardErrorResponse.of("You don't have permission to perform this operation!",
                        ThingsboardErrorCode.PERMISSION_DENIED, HttpStatus.FORBIDDEN)),
                errorResponse("404", "Not Found",
                        ThingsboardErrorResponse.of("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND, HttpStatus.NOT_FOUND)),
                errorResponse("429", "Too Many Requests",
                        ThingsboardErrorResponse.of("Too many requests for current tenant!",
                        ThingsboardErrorCode.TOO_MANY_REQUESTS, HttpStatus.TOO_MANY_REQUESTS))
        );
    }

    private Response errorResponse(String code, String description, ThingsboardErrorResponse example) {
        return errorResponse(code, description,  List.of(errorExample("error-code-" + code, description, example)));
    }

    private Response errorResponse(String code, String description, List<Example> examples) {
        return errorResponse(code, description, examples, ThingsboardErrorResponse.class);
    }

    private Response errorResponse(String code, String description, List<Example> examples,
                                   Class<? extends ThingsboardErrorResponse> errorResponseClass) {
        return new ResponseBuilder()
                .code(code)
                .description(description)
                .examples(examples)
                .representation(MediaType.APPLICATION_JSON)
                .apply(classRepresentation(errorResponseClass, true))
                .build();
    }

    private Example errorExample(String id, String summary, ThingsboardErrorResponse example) {
        return new ExampleBuilder()
                .mediaType(MediaType.APPLICATION_JSON_VALUE)
                .summary(summary)
                .id(id)
                .value(example).build();
    }

    private Consumer<RepresentationBuilder> classRepresentation(Class<?> clazz, boolean isResponse) {
        return r -> r.model(
                m ->
                        m.referenceModel(ref ->
                                ref.key(k ->
                                        k.qualifiedModelName(q ->
                                                q.namespace(clazz.getPackageName())
                                                        .name(clazz.getSimpleName())).isResponse(isResponse)))
        );
    }

    private static class SecurityPathOperationSelector implements Predicate<OperationContext> {

        private final Predicate<String> securityPathSelector;

        SecurityPathOperationSelector(String securityPathRegex, String nonSecurityPathRegex) {
            this.securityPathSelector = (not(regex(nonSecurityPathRegex)));
        }

        @Override
        public boolean test(OperationContext operationContext) {
            return this.securityPathSelector.test(operationContext.requestMappingPattern());
        }
    }

}

以下是YML文件:

swagger:
  auth:
    token_url: ${security.keycloak.serverUrl}/realms/${security.keycloak.realm}/protocol/openid-connect/token/
    auth_url: ${security.keycloak.serverUrl}/realms/${security.keycloak.realm}/protocol/openid-connect/auth/
  api_path_regex: "${SWAGGER_API_PATH_REGEX:/api/(customer|device|user|tenant).*}"
  security_path_regex: "${SWAGGER_SECURITY_PATH_REGEX:/api/(customer|device|user|tenant).*}"
  non_security_path_regex: "${SWAGGER_NON_SECURITY_PATH_REGEX:/api/(?:noauth|v1)/.*}"
  title: "${SWAGGER_TITLE:yousefCo REST API}"
  description: "${SWAGGER_DESCRIPTION: yousefCo open-source IoT platform REST API documentation.}"
  contact:
    name: "${SWAGGER_CONTACT_NAME:yousefCo Team}"
    url: "${SWAGGER_CONTACT_URL:http://iot.test.net}"
    email: "${SWAGGER_CONTACT_EMAIL:info@gmail.com}"
  license:
    title: "${SWAGGER_LICENSE_TITLE:Apache License Version 2.0}"
    url: "${SWAGGER_LICENSE_URL:https://github.com/yousef/yousef/blob/master/LICENSE}"
  version: "${SWAGGER_VERSION:}"
ryoqjall

ryoqjall1#

我认为您混合了Swagger版本2和3之间的配置,您的整体设置配置为在Swagger 3.0中使用,因此
首先,您必须将DocumentationType更改为OAS_30
第二,使用ApiKey安全方案时,请注意,在调用API时,字段名将用作头,因此必须将其更改为:

private ApiKey apiKey() {
    return new ApiKey("X-Authorization", "AnyNameYouWant", "header");
}

请注意,您必须在标记前添加Bearer单词,还必须将名称SecurityReference更改为X-Authorization
第三,如果您需要配置OAuth2安全方案,请使用OAuth2Scheme构建器类和password流,而不是使用OAuth类,如下所示:

return OAuth2Scheme.OAUTH2_PASSWORD_FLOW_BUILDER
        .name("KeycloakAuth")
        .scopes(newArrayList(scopes()))
        .tokenUrl(keycloakAuthTokenUrl)
        .refreshUrl(keycloakAuthTokenUrl)
        .build();

不要忘记将SecurityReference中的相同作用域添加到OAuth2Scheme作用域中。

相关问题