Android OkHttp with Basic Authentication
我将OkHttp库用于新项目,其易用性给我留下了深刻的印象。 我现在需要使用基本身份验证。 不幸的是,缺少示例代码。 我正在寻找一个示例,当遇到HTTP 401标头时如何将用户名/密码凭据传递给OkAuthenticator。 我查看了这个答案:
使用基本HTTP身份验证改进POST请求:"无法重试流式HTTP正文"
但这并没有使我走得太远。 OkHttp github存储库上的示例也没有基于身份验证的示例。 有没有人有要点或其他代码示例来指导我正确的方向? 感谢你的协助!
okhttp3的更新代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | 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; } } |
这是更新的代码:
1 2 3 4 5 6 7 8 9 10 11 12 | 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; } }) |
正如@agamov指出的那样:
The aforementioned solution has one drawback: httpClient adds
authorization headers only after receiving 401 response
@agamov建议然后"手动"向每个请求添加身份验证头,但是有一个更好的解决方案:使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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客户端即可:
1 2 3 | OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(new BasicAuthInterceptor(username, password)) .build(); |
上述解决方案有一个缺点:
httpClient仅在收到401响应后才添加授权标头。
这是我与api-server的通讯样子:
如果您需要为每个请求使用basic-auth,则最好将auth-header添加到每个请求中,或使用如下的包装方法:
1 2 3 4 5 6 | 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(); } |
尝试使用OkAuthenticator:
1 2 3 4 5 6 7 8 9 10 11 | 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具有base 64身份验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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() { ... |
有人要求提供Kotlin版本的拦截器。这是我想出的,效果很好:
1 2 3 4 5 6 7 8 | 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() |
就我而言,仅当我将授权集成到标头(OkHttp版本4.0.1)中时,它才起作用:
1 2 3 4 5 6 | Request request = new Request.Builder() .url("www.url.com/api") .addHeader("Authorization", Credentials.basic("username","password")) .build(); Request response = client.newCall(request).execute(); |
我注意到在Android上使用某些服务器API(例如django)时,您应该在令牌中添加一个单词
1 2 3 4 | Request request = new Request.Builder() .url(theUrl) .header("Authorization","Token 6utt8gglitylhylhlfkghriyiuy4fv76876d68") .build(); |
,其中有问题的词是"令牌"。总体而言,您应该仔细查看那些特定服务器API的规则,以了解如何撰写请求。
在OkHttp3中,您可以通过添加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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请求进行垃圾邮件处理,则可以使用一种称为"预身份验证"的方法,在该方法中,发送
1 2 3 4 5 6 | String credentials = Credentials.basic("username","password"); Request httpRequest = new Request.Builder() .url("some/url") .header("content-type","application/json") .header(HttpHeaders.AUTHORIZATION, credentials) .build(); |
所有答案都很好,但是没有人说过,对于某些请求内容类型是必需的,您应该像这样向您的请求添加内容类型:
1 2 3 4 5 | Request request = new Request.Builder() .url(url) .addHeader("content-type","application/json") .post(body) .build(); |
如果不添加它,则会收到未授权消息,并且会浪费大量时间来修复它。
这是OkHttp Client的代码段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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(); |
立即提出要求。基本身份验证将继续进行,因为客户端已经拥有了。
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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()); } }); |