【问题标题】:Swagger Codegen CLI Java Client - How to use it rightSwagger Codegen CLI Java 客户端 - 如何正确使用它
【发布时间】:2017-02-05 01:13:15
【问题描述】:

我目前正在玩我的 jersey2 休息服务。为了更好地了解给定服务(描述、类型等),我大量使用了 swagger (swagger-jersey2-jaxrs)。 所以我可以生成我的服务描述 (swagger.json),我可以通过 swagger ui 查看和探索它们。

现在我需要创建一些客户端来使用这些服务。我遇到了 swagger codegen cli,它是一个很好的工具,可以生成你的客户端和许多不同的语言(在我的例子中是 java)。我能够生成 api 客户端和正在使用的模型。

在这里我面临第一个问题。 REST 服务和 swagger 描述受 http 基本身份验证保护。我读了documentation,它给了我一些暗示,可以使用基本身份验证。在这一点上,我不得不提到,从我的角度来看,文档非常差。它说:

-a , --auth 远程获取 swagger 定义时添加授权标头。传入一个 URL 编码的 name:header 字符串,用逗号分隔多个值。

我想到的第一件事是在 http 标头中传递一个字符串,但这不起作用,甚至谷歌搜索如何在 swagger cli 中使用基本身份验证也没有得到一些明确的答案。经过大量尝试和错误后,我(我使用的是 CLI 2.1.2)终于找到了正确的格式:

java -jar swagger-codegen-cli-2.1.2.jar generate -a "Authorization: Basic YWRtaW46YWRtaW4=" -i http://localhost:8080/webproject/restapi/swagger.json -l java -o restclient

在我的例子中,YWRtaW46YWRtaW4= 是 admin:admin 的 base64 编码值。

到目前为止一切顺利。生成的 java 客户端也必须使用基本身份验证。我查看了 ApiClient 中的方法,发现了 setUsername 和 setPassword。我认为这种方法使客户能够使用基本身份验证,但没有运气。

所以我深入研究了生成的类,尤其是 ApiClient 和几个生成的 ApiService 类。 我发现 setUsername 和 setPassword 无效,原因如下:

/**
   * Helper method to set username for the first HTTP basic authentication.
   */
  public void setUsername(String username) {
    for (Authentication auth : authentications.values()) {
      if (auth instanceof HttpBasicAuth) {
        ((HttpBasicAuth) auth).setUsername(username);
        return;
      }
    }
    throw new RuntimeException("No HTTP basic authentication configured!");
  }

其中HashMap同时定义如下:

// Setup authentications (key: authentication name, value: authentication).
authentications = new HashMap<String, Authentication>();
// Prevent the authentications from being modified.
authentications = Collections.unmodifiableMap(authentications);

身份验证 hashmap 变得不可变,但为什么呢?目的是什么?此外,ApiClinet 内部没有生成所需身份验证对象的辅助方法,因此我执行了以下操作:

1) 注释掉 authentications Collections.unmodifiableMap(authentications) 行,以便哈希图再次变为可修改

2) 手动创建所需的身份验证对象

HttpBasicAuth authentication = new HttpBasicAuth(); 
authentication.setUsername("admin");
authentication.setPassword("admin");

3) 将 auth 对象添加到 apiClients 身份验证 hashmap:

ApiClient apiClient = new ApiClient();
apiClient.setBasePath(basePath);
apiClient.getAuthentications().put("basic", authentication);

4)修改invokeApi方法(ApiClient.java)

public String invokeAPI(String path, String method, Map<String, String> queryParams, Object body, Map<String, String> headerParams, Map<String, String> formParams, String accept, String contentType, String[] authNames) throws ApiException {
String authNames2[] = {"basic"};
updateParamsForAuth(authNames2, queryParams, headerParams);
//updateParamsForAuth(authNames, queryParams, headerParams);
...

第 4 步是必要的,因为 ApiServices 调用 apiClient 方法如下:

String[] authNames = new String[] {  };
String response = apiClient.invokeAPI(path, "POST", queryParams, postBody, headerParams, formParams, accept, contentType, authNames);

另一种可能的解决方案是在每个 apiService 中定义身份验证哈希映射的密钥,例如:

String[] authNames = new String[] { "basic" };

完成所有修改后,一切都按预期工作,但我不认为这是自动生成的休息客户端背后的想法。 所以我的问题是:我是否遗漏了一些要点,或者我是否应该将大摇大摆的生成客户端(在本例中为 java)更多地考虑为正在开发的 beta 解决方案? 请让我正确,我认为整个 swagger 框架(jersey2 支持、openapi、swaggerui、codegen)是一件很棒的事情,我感谢开发人员的努力,但我想正确使用 codegen,我不认为背后的想法是所以必须以这种方式自定义生成的ApiClient和ApiServices。

【问题讨论】:

    标签: java rest swagger swagger-2.0 swagger-codegen


    【解决方案1】:

    问题在于您的规范没有提及您要使用的安全类型(也称为安全定义)或哪个安全定义适用于哪个端点。

    swagger 规范是 here,但它并不能说明全部情况。

    您需要做的是 1. 设置安全定义。这是一个简单的基本 http auth 定义:

    securityDefinitions:
      basic:
        type: basic
        description: HTTP Basic Authentication. 
    

    和 2. 在端点中使用该安全定义。

    paths:
      /:
        get:
          security:
           - basic: []
          responses:
            200:
              description:  OK
    

    然后重新生成您的 swagger 客户端代码。它应该正确设置不可变映射和 authNames 数组。

    【讨论】:

    • 是的,但是这样做时生成的代码不是线程安全的,这意味着我们对于每个请求只有一个用户名/密码。如果您将请求代理到另一个 API,并且需要根据当前用户动态更改身份验证,则您不能使用它...如果 authentications Map 是可变的,您至少可以添加自己的类(实现身份验证接口)和无论如何进行身份验证。例如:调用服务以获取当前用户的用户名和密码,并生成 BasicAuth 标头以添加到请求中。
    • 添加您自己的身份验证类对我来说听起来不错。你有如何做到这一点的例子吗?
    • 这里建议github.com/swagger-api/swagger-codegen/issues/1435 我相信APIClient 的构建器方法可以解决这个问题。现在作为一种快速解决方法,我只是自定义 mustache 模板并删除 authentications = Collections.unmodifiableMap(authentications); 部分,然后在 Map&lt;String, Authentication&gt; authentications 中用我自己的替换基本身份验证实现
    【解决方案2】:

    如前所述,如果您不想修改现有代码,可以在自定义配置中扩展 ApiClient,例如

    @Configuration
    public class Config {
    
      @Value("${baseUrl}")
      private String baseUrl;
    
      protected class AuthApiClient extends ApiClient {
    
        public AuthApiClient() {
          super();
        }
    
        @Override
        public <T> T invokeAPI(final String path, final HttpMethod method,
                final MultiValueMap<String, String> queryParams, final Object body,
                final HttpHeaders headerParams, final MultiValueMap<String, Object> formParams,
                final List<MediaType> accept, final MediaType contentType,
                final String[] authNames, final ParameterizedTypeReference<T> returnType)
                throws RestClientException {
    
                final HttpBasicAuth auth = new HttpBasicAuth();
                auth.setUsername("myUsername");
                auth.setPassword("myPassword");
                auth.applyToParams(queryParams, headerParams);
    
          return super.invokeAPI(path, method, queryParams, body, headerParams, formParams,
                    accept, contentType, authNames, returnType);
        }
      }
    
      @Bean
      @Primary
      @Qualifier("MyApiClient")
      public AuthApiClient myApiClient() {
        final AuthApiClient apiClient = new AuthApiClient();
        apiClient.setBasePath(this.baseUrl);
        return apiClient;
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2017-07-15
      • 2018-09-27
      • 2017-08-20
      • 2018-08-29
      • 2018-02-11
      • 2019-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多