我目前正在使用jersey2休息服务。为了更好地了解给定的服务(描述,类型等),我大量使用了swagger(swagger-jersey2-jaxrs)。因此,我能够生成我的服务描述(swagger.json),并且可以通过swagger ui查看和浏览它们。
现在,我需要创建一些客户端以使用这些服务。我来了accrooss swagger codegen cli,这是生成客户端和许多不同语言(在我的情况下为Java)的好工具。我能够生成api客户端和使用中的模型。
在这里,我遇到了第一个问题。REST服务和详细描述受http basic auth保护。我阅读了文档 ,这给了我一些暗示,即可以使用基本身份验证。在这一点上,我不得不提到,从我的角度来看,这种做法非常糟糕。它说:
-a,--auth在远程获取swagger定义时添加授权标头。传递URL编码的name:header字符串,并用逗号分隔多个值。
我想到的第一件事是像http标头中那样传递一个字符串,但是那行不通,甚至谷歌搜索如何将基本auth与swagger cli一起使用也没有得到明确的答案。经过大量的尝试和错误后,我(我使用的是CLI 2.1.2)最终确定了正确的格式,例如:
java -jar swagger-codegen-cli-2.1.2.jar generate -a“授权:基本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内部没有生成所需的auth对象的辅助方法,因此我做了以下工作:
1)注释掉行身份验证Collections.unmodifiableMap(authentications),以便使哈希图再次可修改
2)手动创建所需的身份验证对象
HttpBasicAuth authentication = new HttpBasicAuth();
authentication.setUsername("admin");
authentication.setPassword("admin");
3)将auth对象添加到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);
...
步骤4是必需的,因为ApiServices如下调用apiClient方法:
String[] authNames = new String[] { };
String response = apiClient.invokeAPI(path, "POST", queryParams, postBody, headerParams, formParams, accept, contentType, authNames);
另一种可能的解决方案是在每个apiService中为身份验证哈希图定义Key auf,例如:
String[] authNames = new String[] { "basic" };
完成所有修改后,所有功能均按预期工作,但我无法想到这是自动生成的休息客户端背后的想法。所以我的问题是:我是否遗漏了一点?还是应该考虑一下由swagger生成的客户端(在本例中为Java)更多正在开发的beta解决方案?请正确理解,我认为整个swagger框架(球衣2支持,openapi,swaggerui,codegen)是一件很棒的事情,我感谢开发人员的努力,但我想正确使用codegen,我不认为背后的想法是因此必须以这种方式自定义生成的ApiClient和ApiServices。
问题在于您的规范中没有提及您要使用的安全性类型(也称为安全性定义),也没有提及哪个安全性定义适用于哪个端点。
昂首阔步的规格在这里,但并不能说明全部。
您需要做的是1.设置安全定义。这是一个简单的基本http auth定义:
securityDefinitions:
basic:
type: basic
description: HTTP Basic Authentication.
2.在端点中使用该安全性定义。
paths:
/:
get:
security:
- basic: []
responses:
200:
description: OK
然后重新生成您的招摇的客户端代码。它应该正确设置不可变映射和authNames数组。
是的,但是这样做时生成的代码不是线程安全的,这意味着每个请求只有一个用户名/密码。如果您正在将请求代理到另一个API,并且需要根据当前用户动态更改身份验证,则不能使用该身份验证...如果
authentications
Map可变,则至少可以添加自己的类(实现身份验证接口)并以任何方式进行身份验证想。例如:调用服务以获取当前用户的用户名和密码,并生成BasicAuth标头以添加到请求中。添加您自己的身份验证类对我来说听起来不错。你有一个如何做到这一点的例子吗?
正如在这里github.com/swagger-api/swagger-codegen/issues/1435所建议的,我相信针对
APIClient
该问题的构建器方法可以解决该问题。现在,作为一种快速的解决方法,我只是自定义小胡子模板并删除该authentications = Collections.unmodifiableMap(authentications);
部分,然后在其中用我自己的替换基本的auth实现。Map<String, Authentication> authentications