私は現在、jersey2RESTサービスで遊んでいます。与えられたサービス(説明、タイプなど)のより良い概要のために、私はswagger(swagger-jersey2-jaxrs)を多用します。そのため、サービスの説明(swagger.json)を生成でき、swaggeruiを介してそれらを表示および探索できます。
今、私はこれらのサービスを利用するためにいくつかのクライアントを作成する必要がある時点にいます。私はあなたのクライアントと多くの異なる言語(私の場合はJava)を生成するための素晴らしいツールであるaccrooss swagger codegencliに来ました。使用中のAPIクライアントとモデルを生成できます。
ここで私は最初の問題に遭遇しました。 RESTサービスとSwaggerの説明はhttp基本認証で保護されています。 ドキュメント を読んだところ、基本認証を使用する可能性があるというヒントが得られました。これでポイント私の観点からは、ドキュメンテーションは非常に貧弱であることに言及する必要があります。
-a、-authは、Swagger定義をリモートでフェッチするときに、認証ヘッダーを追加します。複数の値をコンマで区切って、名前:ヘッダーのURLエンコードされた文字列を渡します。
私が最初に考えたのは、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);
認証ハッシュマップは不変になりますが、なぜですか?目的は何ですか?さらに、必要な認証オブジェクトを生成するApiClinet内にヘルパーメソッドがないため、次のことを行いました。
1)行authentications Collections.unmodizableMap(authentications)をコメントアウトして、ハッシュマップが再び変更可能になるようにします
2)必要な認証オブジェクトを手動で作成する
HttpBasicAuth authentication = new HttpBasicAuth();
authentication.setUsername("admin");
authentication.setPassword("admin");
3)認証オブジェクトをapiClients認証ハッシュマップに追加します。
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);
...
ApiServicesは次のようにapiClientメソッドを呼び出すため、手順4が必要です。
String[] authNames = new String[] { };
String response = apiClient.invokeAPI(path, "POST", queryParams, postBody, headerParams, formParams, accept, contentType, authNames);
他の可能な解決策は、次のようにすべてのapiServiceで認証ハッシュマップのキーを定義することです。
String[] authNames = new String[] { "basic" };
すべての変更を行った後、すべてが期待どおりに機能しますが、これが自動生成されたRESTクライアントの背後にある考え方であるとは思えません。だから私の質問は:私はいくつかのポイントを逃していますか、それともSwaggerで生成されたクライアント(この場合はJava)を開発中のベータソリューションのことをもっと考えるべきですか?正しく理解してください。Swaggerフレームワーク全体(jersey2サポート、openapi、swaggerui、codegen)は素晴らしいことだと思います。開発者の努力に感謝しますが、codegenを正しく使用したいので、背後にある考え方はしたがって、生成されたApiClientとApiServicesをそのような方法でカスタマイズする必要があります。
問題は、仕様に、使用するセキュリティのタイプ(別名、セキュリティ定義)や、どのセキュリティ定義がどのエンドポイントに適用されるかが記載されていないことです。
スワッガーの仕様は ここ ですが、全体像を伝えるものではありません。
あなたがする必要があることは1です。セキュリティ定義を設定します。簡単な基本http認証の定義は次のとおりです。
securityDefinitions:
basic:
type: basic
description: HTTP Basic Authentication.
および2.エンドポイントでそのセキュリティ定義を使用します。
paths:
/:
get:
security:
- basic: []
responses:
200:
description: OK
次に、Swaggerクライアントコードを再生成します。不変マップとauthNames配列を正しく設定する必要があります。
すでに提案したように、既存のコードを変更したくない場合は、カスタム構成で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;
}
}