繁体   English   中英

来自 java.net.http.HttpClient 调用的不可靠响应使用 Java 11 和 Spring 启动

[英]Unreliable response from java.net.http.HttpClient call using Java 11 and Spring Boot

我正在 Spring Boot 中编写一个后端应用程序,该应用程序从第三方调用另一个 API。

我遇到了这个特定调用的问题,它检索了一个包含不记名令牌的令牌 object,然后我在他们的其他端点中使用它。 在调用其他端点时,检索到的令牌有时会起作用,但大多数时候不会,从而导致未经授权的响应。

@RestController
public class CotizacionController {

    Logger logger = LoggerFactory.getLogger(CotizacionController.class);

    @Value("${service.credentials.tokenServer}")
    private String tokenServer;

    @Value("${service.credentials.grantType}")
    private String grantType;

    @Value("${service.credentials.username}")
    private String username;

    @Value("${service.credentials.password}")
    private String password;

    HttpClient client = HttpClient.newHttpClient();

    @RequestMapping("/create")
    public Object Create() throws IOException, InterruptedException {

        HashMap<String, String> parameters = new HashMap<>();
        parameters.put("grant_type", grantType);
        parameters.put("username", username);
        parameters.put("password", password);

        String form = parameters.keySet().stream()
                .map(key -> key + "=" 
                                + URLEncoder.encode(parameters.get(key), 
                                                    StandardCharsets.UTF_8))
                .collect(Collectors.joining("&"));

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(tokenServer))
                .header("Content-Type", "application/x-www-form-urlencoded")
                .POST(BodyPublishers.ofString(form)).build();

        HttpResponse<?> response = client.send(request, BodyHandlers.ofString());

        TokenResponse result = new ObjectMapper().readValue(response
                                   .body().toString(), TokenResponse.class);

        return result;
    }

}

这是一个示例令牌 object:

{
    "access_token": "z-bu-Pde6M2dlPiaRzd5XpTrT7ohpFQZe157HHVLfdKJWsdmKCloK7AYGEw7SLCe28tjYAxo8MZOE_3W00HEa-bqgUvcrAKfxIubAq0UGXv7jLPWbRwWzhAUCDon3kdstUrJ_OKRN2y26W6qyDBGDqlP5NRSF4unH_pD_ShmpDlSxZdYUqD0da5Y2_uO6YRs5GuWA7XhI9sPa98SxuXN_dwiDJVif418xK646fUgWR8",
    "token_type": "bearer",
    "expires_in": "3599"
}

使用postman检索令牌可以正常工作,因此第三方 API 不会有问题。 我也在.NET Core 3 中实现了同样的服务,它在那里也可以正常工作。

最让我困惑的是,实际的 HttpClient 调用有效,我确实得到了正确的 Json,它映射到我的 TokenResponse object 就好了。 只是令牌值无效......有时。

我也尝试过使用 RestTemplate 和 WebClient Spring 库,但结果是一样的。 通话有效,但检索到的令牌无效。

起初我以为我遇到了竞争条件,因为最初我在那里有另一个 HttpClient 和另一个端点,使用来自令牌调用的响应。 所以我将其简化为仅令牌调用,并将令牌值手动复制到 postman 请求中。 没用。

然后我想也许我的 HttpClient 授权 header 格式不正确,但这是不正确的,因为使用 postman 请求将令牌复制到受保护的端点就表明令牌不起作用。

以及我尝试过的其他事情:

  • 将我在 controller 中生成的表单字符串粘贴到 Postman 请求中,以确保它有效。
  • 检查 URLEncoder 没有弄乱任何表单值。
  • 从令牌 object 复制令牌值,以在另一个端点中使用 Postman。
  • 跳过 object 映射并返回一个简单的字符串,然后手动从 Postman 的响应中复制令牌值,以便我可以在另一个端点中使用它。

在这一点上我很迷茫,唯一想到的是,也许 HttpClient.send() 方法可能正在以一种可能会影响内容的方式解析正文? 我对此表示怀疑,但我看不出还会发生什么。

解决方案与 cookie 有关!

令牌服务器响应正在发送 2 个set-cookie标头,这些标头在 Postman 和 .NET Core 中被自动处理并设置为后续的 HTTP 请求。 第 3 方 API 位于负载均衡器后面并生成这些 session cookies。

我通过在我的main方法中使用以下代码实现系统范围的CookieHandler解决了这个问题。

public static void main(String[] args) {
        CookieHandler.setDefault(new CookieManager());

        SpringApplication.run(Main.class, args);
    }

然后像这样构建我的 HttpClient object:

...
HttpClient client = HttpClient.newBuilder().cookieHandler(CookieHandler.getDefault()).build();
...

这样,响应set-cookie和请求cookie标头将被自动处理,并在此 HttpClient 进行的所有调用中起作用。

默认情况下,CookieHandler 是使用CookiePolicy.ACCEPT_ORIGINAL_SERVER参数创建的。 我的理解是,这使得 cookies 仅在同一主机设置和请求时才起作用。 查看文档以获取有关CookiePolicy的更多选项

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM