簡體   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