Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/398.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用java 11和Spring Boot从java.net.http.HttpClient调用得到的响应不可靠_Java_Spring Boot_Http_Java 11_Java Http Client - Fatal编程技术网

使用java 11和Spring Boot从java.net.http.HttpClient调用得到的响应不可靠

使用java 11和Spring Boot从java.net.http.HttpClient调用得到的响应不可靠,java,spring-boot,http,java-11,java-http-client,Java,Spring Boot,Http,Java 11,Java Http Client,我正在用Spring Boot编写一个后端应用程序,它调用来自第三方的另一个API 我在这个调用中遇到了一个问题,它检索一个包含承载令牌的令牌对象,然后我在它们的其他端点中使用它。 调用其他端点时,检索到的令牌有时有效,但大多数情况下无效,从而导致未经授权的响应 @RestController public class CotizacionController { Logger logger = LoggerFactory.getLogger(CotizacionController.

我正在用Spring Boot编写一个后端应用程序,它调用来自第三方的另一个API

我在这个调用中遇到了一个问题,它检索一个包含承载令牌的令牌对象,然后我在它们的其他端点中使用它。 调用其他端点时,检索到的令牌有时有效,但大多数情况下无效,从而导致未经授权的响应

@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;
    }

}
使用邮递员检索代币工作得非常好,因此第三方API不会出现问题。我也在.NET Core 3中实现了这个相同的服务,而且它在那里也运行得非常好

最让我困惑的是,实际的HttpClient调用是有效的,我确实得到了一个正确的Json,它映射到了我的TokenResponse对象。只是令牌值无效。。。有时候

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

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

然后我想可能我的HttpClient授权头的格式不正确,但这是不正确的,因为使用postman请求简单地将令牌复制到受保护的端点表明令牌不起作用

还有我尝试过的其他事情:

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

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

解决方案与cookies有关

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

我在
main
方法中使用以下代码实现了一个系统范围的解决方案

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

        SpringApplication.run(Main.class, args);
    }
然后像这样构建我的HttpClient对象:

...
HttpClient client = HttpClient.newBuilder().cookieHandler(CookieHandler.getDefault()).build();
...
这样,响应
set cookie
和请求
cookie
头将自动处理,并在该HttpClient发出的所有调用中工作


默认情况下,CookieHandler是使用
CookiePolicy.ACCEPT_ORIGINAL_SERVER
参数创建的。我的理解是,只有在同一主机设置并请求cookie时,cookie才能工作。查看文档以获取更多选项,关于

您是否有办法验证您收到的访问令牌是否与生成的访问令牌相同(发送帖子的位置)?
HttpClient.send()
不解析正文:它应该只发送字节,正如您所期望的那样。但是请注意,默认情况下,
bodypublisher.ofString(String)
将字节编码为UTF-8。和
BodyHandlers.ofString()
将使用响应头中指示的任何字符集(如果未指定任何字符集,则使用UTF-8)。记录请求/响应头以尝试找出是否有任何奇怪的地方可能会很有趣:
-Djdk.httpclient.httpclient.log=“errors,requests,headers”
令牌端点请求和响应是相同的,但我在查看受保护端点的请求头时发现了线索。它有一些cookie是由令牌端点的响应设置的,postman和.Net Core都在自动处理这些cookie,但java HttpClient没有。在尝试受保护的端点之前,我通过手动删除cookies在postman上复制了这个问题,并且得到了相同的问题行为。如果结果与cookies有关,那么在构建客户端时尝试一下。考虑添加一个显示您的解决方案的答案,以防将来其他人遇到同样的问题。
...
HttpClient client = HttpClient.newBuilder().cookieHandler(CookieHandler.getDefault()).build();
...