Warm tip: This article is reproduced from stackoverflow.com, please click
java rest swagger swagger-2.0 swagger-codegen

Swagger Codegen CLI Java Client

发布于 2020-05-10 22:35:04

I'm currently playing around with my jersey2 rest service. For a better overview of the given service (description, types and so on) I make heavily use of swagger (swagger-jersey2-jaxrs). So I'm able to genereate my service description (swagger.json) and I can view and explore them via swagger ui.

Now I'm at the point that I need to create some clients to make use of these services. I came accrooss swagger codegen cli which is a nice tool to generate your client and many different languages (java in my case). I'm able to generate the api client and the model in use.

Here I came accross the first problem. The REST services and the swagger description are http basic auth protected. I read the documentation which gave me some hint that there is a possibility to use basic auth. At this point I have to mention that from my point of view the doucmentation is very poor. It says:

-a , --auth adds authorization headers when fetching the swagger definitions remotely. Pass in a URL-encoded string of name:header with a comma separating multiple values.

First thing I thoght of is to pass a string like in an http header but that doen't work and even googling how to use basic auth with swagger cli didn't result in some clear answser. After a lot of try and errors I (I'm using CLI 2.1.2) I finaly came accross the right format, eg:

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

where YWRtaW46YWRtaW4= is the the base64 encoded value of admin:admin in my case.

So far so good. The generated java client have to make use of basic auth as well. I took a look at the methods from the ApiClient and found setUsername and setPassword. I thought that this methods empoweres the client to use basic auth but no luck.

So I took a deeper look at the generated classes especially the ApiClient and the several generated ApiService Classes. I found out that the setUsername and setPassword have no effect because of the following:

/**
   * 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!");
  }

where at the same time the HashMap is defined as the following:

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

The authentication hashmap becomes immutable, but why? What is the purpuse? Furthermore there are no helper methods inside the ApiClinet which generates the needed auth object so I did the follwing:

1) comment out the line authentications Collections.unmodifiableMap(authentications) so the hashmap becomes modifiable again

2) create needed auth object manualy

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

3) add the auth object to the apiClients authentication hashmap:

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

4) modifying invokeApi Method (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);
...

Step 4 is necessary because the ApiServices invoke the apiClient method like the following:

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

An other possible solution would be to define the Key auf the authentications hashmap in every apiService like:

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

After doing all the modifications everything works as expected but I cant think of that this is the idea behind an autogenerated rest client. So my question is: Am I missing some point or should I think of the swagger generated client (java in this case) more of a beta solution which is under development? Please get me right, I think the whole swagger framework (jersey2 support, openapi, swaggerui, codegen) is a great thing and I appreciate the developers effort but I want to use the codegen right and I don't think that the idea behind is so have to customize the generated ApiClient and ApiServices in such a way.

Questioner
Matthias
Viewed
13
Taylor 2017-01-19 11:21

The issue is that your specification does not mention the types of Security you want to use (a.k.a. Security Definitions) or which Security Definition applies to which end point.

The swagger specification is here, but it doesn't tell the whole story.

What you need to do is 1. Set up the Security Definitions. Here is a simple basic http auth definition:

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

and 2. Use that security definition in the end point.

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

Then regenerate your swagger client code. It should set up the immutable map correctly and the authNames array.