我将OkHttp库用于新项目,其易用性给我留下了深刻的印象。我现在需要使用基本身份验证。不幸的是,缺少示例代码。我正在寻找一个在遇到HTTP 401标头时如何将用户名/密码凭据传递给OkAuthenticator的示例。我看了这个答案:
使用基本HTTP身份验证改进POST请求:“无法重试流式HTTP正文”
但这并没有使我走得太远。OkHttp github存储库上的示例也没有基于身份验证的示例。有没有人有要点或其他代码示例来指导我正确的方向?感谢你的协助!
我将OkHttp库用于新项目,其易用性给我留下了深刻的印象。我现在需要使用基本身份验证。不幸的是,缺少示例代码。我正在寻找一个在遇到HTTP 401标头时如何将用户名/密码凭据传递给OkAuthenticator的示例。我看了这个答案:
使用基本HTTP身份验证改进POST请求:“无法重试流式HTTP正文”
但这并没有使我走得太远。OkHttp github存储库上的示例也没有基于身份验证的示例。有没有人有要点或其他代码示例来指导我正确的方向?感谢你的协助!
Answers:
尝试使用OkAuthenticator:
client.setAuthenticator(new OkAuthenticator() {
@Override public Credential authenticate(
Proxy proxy, URL url, List<Challenge> challenges) throws IOException {
return Credential.basic("scott", "tiger");
}
@Override public Credential authenticateProxy(
Proxy proxy, URL url, List<Challenge> challenges) throws IOException {
return null;
}
});
更新:
重命名为Authenticator
okhttp3的更新代码:
import okhttp3.Authenticator;
import okhttp3.Credentials;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.Route;
public class NetworkUtil {
private final OkHttpClient.Builder client;
{
client = new OkHttpClient.Builder();
client.authenticator(new Authenticator() {
@Override
public Request authenticate(Route route, Response response) throws IOException {
if (responseCount(response) >= 3) {
return null; // If we've failed 3 times, give up. - in real life, never give up!!
}
String credential = Credentials.basic("name", "password");
return response.request().newBuilder().header("Authorization", credential).build();
}
});
client.connectTimeout(10, TimeUnit.SECONDS);
client.writeTimeout(10, TimeUnit.SECONDS);
client.readTimeout(30, TimeUnit.SECONDS);
}
private int responseCount(Response response) {
int result = 1;
while ((response = response.priorResponse()) != null) {
result++;
}
return result;
}
}
never give up
评论XD
正如@agamov指出的那样:
上述解决方案有一个缺点:httpClient仅在收到401响应后才添加授权标头
@agamov建议然后“手动”向每个请求添加身份验证标头,但是有一个更好的解决方案:使用 Interceptor
:
import java.io.IOException;
import okhttp3.Credentials;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
public class BasicAuthInterceptor implements Interceptor {
private String credentials;
public BasicAuthInterceptor(String user, String password) {
this.credentials = Credentials.basic(user, password);
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request authenticatedRequest = request.newBuilder()
.header("Authorization", credentials).build();
return chain.proceed(authenticatedRequest);
}
}
然后,只需将拦截器添加到您将用于发出所有已认证请求的OkHttp客户端:
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new BasicAuthInterceptor(username, password))
.build();
这是更新的代码:
client.setAuthenticator(new Authenticator() {
@Override
public Request authenticate(Proxy proxy, Response response) throws IOException {
String credential = Credentials.basic("scott", "tiger");
return response.request().newBuilder().header("Authorization", credential).build();
}
@Override
public Request authenticateProxy(Proxy proxy, Response response) throws IOException {
return null;
}
})
前述解决方案具有一个缺点:httpClient仅在收到401响应后才添加授权标头。这是我与api-server的通讯样子:
如果您需要为每个请求使用basic-auth,则最好将auth-header添加到每个请求中,或使用如下的包装方法:
private Request addBasicAuthHeaders(Request request) {
final String login = "your_login";
final String password = "p@s$w0rd";
String credential = Credentials.basic(login, password);
return request.newBuilder().header("Authorization", credential).build();
}
Okhttp3具有base 64身份验证
String endpoint = "https://www.example.com/m/auth/"
String username = "user123";
String password = "12345";
String credentials = username + ":" + password;
final String basic =
"Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
Request request = new Request.Builder()
.url(endpoint)
.header("Authorization", basic)
.build();
OkHttpClient client = SomeUtilFactoryClass.buildOkhttpClient();
client.newCall(request).enqueue(new Callback() {
...
okhttp3.Credentials.basic(user, pass)
这样做,所以我认为应该首选(只是因为这样会减少维护的代码)。
okhttp3.Credentials.basic(user, pass)
不会发出任何请求或更改任何行为,它只是将用户名和密码转换为基本的身份验证字符串。
有人要求提供Kotlin版本的拦截器。这是我想出的,效果很好:
val client = OkHttpClient().newBuilder().addInterceptor { chain ->
val originalRequest = chain.request()
val builder = originalRequest.newBuilder()
.header("Authorization", Credentials.basic("ausername", "apassword"))
val newRequest = builder.build()
chain.proceed(newRequest)
}.build()
在OkHttp3中,您可以OkHttpClient
通过添加authenticator()
方法来对自身设置授权。在您的原始呼叫返回401响应后,the authenticator()
添加Authorization
标题
new OkHttpClient.Builder()
.connectTimeout(10000, TimeUnit.MILLISECONDS)
.readTimeout(10000, TimeUnit.MILLISECONDS)
.authenticator(new Authenticator() {
@Nullable
@Override
public Request authenticate(@NonNull Route route, @NonNull Response response) {
if (response.request().header(HttpHeaders.AUTHORIZATION) != null)
return null; //if you've tried to authorize and failed, give up
String credential = Credentials.basic("username", "pass");
return response.request().newBuilder().header(HttpHeaders.AUTHORIZATION, credential).build();
}
})
.build();
尽管它更安全,但是如果您不想首先对所有401请求进行垃圾邮件处理,则可以使用一种称为“预身份验证”的方法,在该方法中发送 Authorization
请求
String credentials = Credentials.basic("username", "password");
Request httpRequest = new Request.Builder()
.url("some/url")
.header("content-type", "application/json")
.header(HttpHeaders.AUTHORIZATION, credentials)
.build();
我注意到在Android上使用某些服务器API(例如django)时,您应该在令牌中添加一个单词
Request request = new Request.Builder()
.url(theUrl)
.header("Authorization", "Token 6utt8gglitylhylhlfkghriyiuy4fv76876d68")
.build();
,其中有问题的词是“令牌”。总体而言,您应该仔细查看那些特定服务器API的规则,以了解如何撰写请求。
Bearer 6utt8gglitylhylhlfkghriyiuy4fv76876d68
所有答案都很好,但是没有人说,对于某些请求,需要content-type,您应该像这样向您的请求添加content-type:
Request request = new Request.Builder()
.url(url)
.addHeader("content-type", "application/json")
.post(body)
.build();
如果不添加它,则会收到未授权消息,并且会浪费大量时间来修复它。
这是OkHttp Client的代码段:
OkHttpClient client = new OkHttpClient.Builder()
.authenticator(new Authenticator() {
@Override public Request authenticate(Route route, Response
response) throws IOException {
if (response.request().header("Authorization") != null) {
return null; // Give up, we've already attempted to
authenticate.
}
System.out.println("Authenticating for response: " + response);
System.out.println("Challenges: " + response.challenges());
String credential = Credentials.basic(username, password);
return response.request().newBuilder()
.header("Authorization", credential)
.build();
}
}) .build();
立即提出要求。基本身份验证将继续进行,因为客户端已经拥有了。
Request request = new Request.Builder().url(JIRAURI+"/issue/"+key).build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
System.out.println("onFailure: "+e.toString());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println( "onResponse: "+response.body().string());
}
});