gpt4 book ai didi

android - 使用摘要式 HTTP 身份验证 : “Cannot retry streamed HTTP body” 改造 POST 请求

转载 作者:太空狗 更新时间:2023-10-29 13:22:37 25 4
gpt4 key购买 nike

我正在尝试使用 Retrofit 实现摘要式身份验证。我的第一个解决方案在 OkHttpClient 上设置了 OkHttp 的 Authenticator 的实现:

class MyAuthenticator implements Authenticator {
private final DigestScheme digestScheme = new DigestScheme();
private final Credentials credentials = new UsernamePasswordCredentials("user", "pass");

@Override public Request authenticate(Proxy proxy, Response response) throws IOException {
try {
digestScheme.processChallenge(new BasicHeader("WWW-Authenticate", response.header("WWW-Authenticate")));
HttpRequest request = new BasicHttpRequest(response.request().method(), response.request().uri().toString());
String authHeader = digestScheme.authenticate(credentials, request).getValue();
return response.request().newBuilder()
.addHeader("Authorization", authHeader)
.build();
} catch (Exception e) {
throw new AssertionError(e);
}
}

@Override public Request authenticateProxy(Proxy proxy, Response response) throws IOException {
return null;
}
}

这非常适用于通过 Retrofit 进行的 GET 请求。但是,如 this StackOverflow question 中所述, POST 请求导致“无法重试流式传输 HTTP 正文”异常:

Caused by: java.net.HttpRetryException: Cannot retry streamed HTTP body
at com.squareup.okhttp.internal.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:324)
at com.squareup.okhttp.internal.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:508)
at com.squareup.okhttp.internal.http.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:136)
at retrofit.client.UrlConnectionClient.readResponse(UrlConnectionClient.java:94)
at retrofit.client.UrlConnectionClient.execute(UrlConnectionClient.java:49)
at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:357)
at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:282)
at $Proxy3.login(Native Method)
at com.audax.paths.job.LoginJob.onRunInBackground(LoginJob.java:41)
at com.audax.library.job.AXJob.onRun(AXJob.java:25)
at com.path.android.jobqueue.BaseJob.safeRun(BaseJob.java:108)
at com.path.android.jobqueue.JobHolder.safeRun(JobHolder.java:60)
at com.path.android.jobqueue.executor.JobConsumerExecutor$JobConsumer.run(JobConsumerExecutor.java:172)
at java.lang.Thread.run(Thread.java:841)

Jesse Wilson 解释说我们不能在验证后重新发送我们的请求,因为 POST 正文已经被丢弃了。但是由于摘要认证,我们需要返回的WWW-Authenticate header ,所以我们不能使用RequestInterceptor 来简单地添加一个 header 。也许可以在 RequestInterceptor 中执行单独的 HTTP 请求,并在响应中使用 WWW-Authenticate header ,但这看起来很老套。

有什么办法解决这个问题吗?

最佳答案

作为一种解决方法,我最终用 Apache 的 HttpClient 替换了 OkHttp,它具有内置的摘要身份验证。提供 retrofit.client.Client 的实现,将其请求委托(delegate)给 Apache 的 HttpClient:

import retrofit.client.Client;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.AuthScope;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.HttpClientBuilder;
import retrofit.client.Request;
import retrofit.client.Response;

public class MyClient implements Client {
private final CloseableHttpClient delegate;

public MyClient(String user, String pass, String hostname, String scope) {
Credentials credentials = new UsernamePasswordCredentials(user, pass);
AuthScope authScope = new AuthScope(hostname, 443, scope);

CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(authScope, credentials);

delegate = HttpClientBuilder.create()
.setDefaultCredentialsProvider(credentialsProvider)
.build();
}

@Override public Response execute(Request request) {
//
// We're getting a Retrofit request, but we need to execute an Apache
// HttpUriRequest instead. Use the info in the Retrofit request to create
// an Apache HttpUriRequest.
//
String method = req.getMethod();

ByteArrayOutputStream bos = new ByteArrayOutputStream();
if (request.getBody() != null) {
request.getBody().writeTo(bos);
}
String body = new String(bos.toByteArray());

HttpUriRequest wrappedRequest;
switch (method) {
case "GET":
wrappedRequest = new HttpGet(request.getUrl());
break;
case "POST":
wrappedRequest = new HttpPost(request.getUrl());
wrappedRequest.addHeader("Content-Type", "application/xml");
((HttpPost) wrappedRequest).setEntity(new StringEntity(body));
break;
case "PUT":
wrappedRequest = new HttpPut(request.getUrl());
wrappedRequest.addHeader("Content-Type", "application/xml");
((HttpPut) wrappedRequest).setEntity(new StringEntity(body));
break;
case "DELETE":
wrappedRequest = new HttpDelete(request.getUrl());
break;
default:
throw new AssertionError("HTTP operation not supported.");
}

//
// Then execute the request with `delegate.execute(uriRequest)`.
//
// ...
//
}

然后在您的 RestAdapter.Builder 上设置新的客户端实现:

RestAdapter restAdapter = new RestAdapter.Builder()
.setClient(new MyClient("jason", "pass", "something.com", "Some Scope"))
.setEndpoint("https://something.com/api")
.build();

关于android - 使用摘要式 HTTP 身份验证 : “Cannot retry streamed HTTP body” 改造 POST 请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26613933/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com