【问题标题】:Cookie Management For Webflux WebClientWebflux WebClient 的 Cookie 管理
【发布时间】:2019-08-28 16:04:45
【问题描述】:

我有一个 WebClient,它将带有登录凭据的 JSON 对象发送到远程服务器。然后远程服务器返回 cookie。之后,我需要将数据连同 cookie 一起发布到该远程服务器。但是,我无法弄清楚如何在 POST 中重用 cookie。

据我所知,登录响应给出了以下结构 MultiValueMap<String, ResponseCookie>,但是在 POST 上设置 cookie 的代码需要 MultiValueMap<String, String> 或只是 cookie(String, String)

我想我一定是错过了一些转换器魔法,但是什么? 我什至需要退回整个 cookie 吗?

cookie 看起来像这样;

{SSO_Sticky_Session-47873-loadBalancedAdminGrp=[SSO_Sticky_Session-47873-loadBalancedAdminGrp=BNAMAKAKJABP; Path=/; HttpOnly], AUTH_TOKEN=[AUTH_TOKEN=v0l3baVZejIKjdzA1KGpkz4ccnosE6rKLQig1D2bdb-voFmVrF_aaYgzWl3Yc8QK; Path=/], uid=[uid=sjzipQdBtU30OlVbPWtDK2625i24i6t6g3Rjl5y5XcI=; Path=/], __cfduid=[__cfduid=dd872f39fd1d3bfe2a5c7316cd9ff63cd1554623603; Path=/; Domain=.aDomain.net; Max-Age=31535999; Expires=Mon, 6 Apr 2020 07:53:23 GMT; HttpOnly], JSESSIONID=[JSESSIONID=A264A713AD060EE12DA8215AEF66A3C0; Path=/aPath/; HttpOnly]}

我的代码如下。为简洁起见,我删除了内容类型;

WebClient webClient = WebClient.create("https://remoteServer");
MultiValueMap<String, ResponseCookie> myCookies;

webClient
  .post()
  .uri("uri/login")
  .body(Mono.just(myLoginObject), MyLogin.class)
  .exchange()
  .subscribe(r -> 
    System.err.println("Received:" + r.cookies());
    myCookies = r.cookies();
   );

webClient
  .post()
  .uri("/uri/data")
  .cookies(????) // what goes here ??
  .body(....)
  .exchange();

【问题讨论】:

  • 简而言之,我在 post() 的 MultiValueMap 中使用 ResponseCookie.getName() 作为键,使用 ResponseCookie.getValue() 作为键的值。一旦我弄清楚如何预测和处理 cookie 的预期到期时间,我将在下周发布代码。

标签: java spring-webflux


【解决方案1】:

在编写服务器端 Java 和 JSP 多年后,我在很大程度上忽略了 cookie 的概念,因为管理由(例如)服务器端的 Tomcat 和客户端的浏览器负责。在 Spring 中对 cookie 处理的任何搜索总是关注 Spring 服务器,而很少关注 Spring 实际上是另一个服务器的客户端。 WebClient 的任何示例都很简单,并且没有假设任何形式的安全协商。

阅读了 cookie 解释 Wikipedia Cookies 和 cookie 标准 RFC6265,我明白为什么传入的 cookie 在类 ResponseCookie 中,而传出的 cookie 是 String。传入的 cookie 在(例如)DomainPathMax-Age 上有额外的元数据。

对于我的实现,供应商没有指定需要返回哪些 cookie,所以我最终返回了所有这些 cookie。因此,我修改后的代码如下;

WebClient webClient = WebClient.create("https://remoteServer");
MultiValueMap<String, String> myCookies = new LinkedMultiValueMap<String, String>()

webClient
  .post()
  .uri("uri/login")
  .body(Mono.just(myLoginObject), MyLogin.class)
  .exchange()
  .subscribe(r -> 
      for (String key: r.cookies().keySet()) {
        myCookies.put(key, Arrays.asList(r.cookies().get(key).get(0).getValue()));
      }
   );

webClient
  .post()
  .uri("/uri/data")
  .cookies(cookies -> cookies.addAll(myCookies))
  .body(....)
  .exchange();

【讨论】:

  • 有没有人找到更好的方法来处理这件事的cookie?
  • 尽管如此,exchange() 已被标记为已弃用。
【解决方案2】:

由于 .exchange() 已被弃用,但此线程出现在流行的搜索机器上,让我在下面添加一个使用 .exchangeToMono() 的代码示例以供将来参考。

请注意,我使用ExchangeFilterFunction,它将在webClient bean 发送每个请求之前发送授权请求:

@Bean("webClient")
public WebClient webClient(ReactorResourceFactory resourceFactory,
    ExchangeFilterFunction authFilter) {
    var httpClient = HttpClient.create(resourceFactory.getConnectionProvider());
    var clientHttpConnector = new ReactorClientHttpConnector(httpClient);
    return WebClient.builder().filter(authFilter).clientConnector(clientHttpConnector)
        .build();
}

@Bean("authWebClient")
public WebClient authWebClient(ReactorResourceFactory resourceFactory) {
    var httpClient = HttpClient.create(resourceFactory.getConnectionProvider());
    var clientHttpConnector = new ReactorClientHttpConnector(httpClient);
    return WebClient.builder().clientConnector(clientHttpConnector).build();
}

@Bean
public ExchangeFilterFunction authFilter(@Qualifier("authWebClient") WebClient authWebClient,
    @Value("${application.url:''}") String url,
    @Value("${application.basic-auth-credentials:''}") String basicAuthCredentials) {
return (request, next) -> authWebClient.get()
    .uri(url)
    .header("Authorization", String.format("Basic %s", basicAuthCredentials))
    .exchangeToMono(response -> next.exchange(ClientRequest.from(request)
        .headers(headers -> {
            headers.add("Authorization", String.format("Basic %s", basicAuthCredentials));
        })
        .cookies(readCookies(response))
        .build()));
}

private Consumer<MultiValueMap<String, String>> readCookies(ClientResponse response) {
return cookies -> response.cookies().forEach((responseCookieName, responseCookies) ->
    cookies.addAll(responseCookieName,
        responseCookies.stream().map(responseCookie -> responseCookie.getValue())
            .collect(Collectors.toList())));
}

【讨论】:

    猜你喜欢
    • 2018-08-18
    • 2021-08-06
    • 2020-05-18
    • 2018-05-09
    • 2019-12-04
    • 2019-06-04
    • 2021-08-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多