Programing

기본 인증을 사용하는 Android OkHttp

lottogame 2020. 12. 6. 20:51
반응형

기본 인증을 사용하는 Android OkHttp


새 프로젝트에 OkHttp 라이브러리를 사용하고 있으며 사용 편의성에 깊은 인상을 받았습니다. 이제 기본 인증을 사용해야합니다. 불행히도 작동하는 샘플 코드가 부족합니다. HTTP 401 헤더가 발생할 때 사용자 이름 / 암호 자격 증명을 OkAuthenticator에 전달하는 방법의 예를 찾고 있습니다. 나는이 대답을 보았다 :

기본 HTTP 인증을 사용한 Retrofit POST 요청 : "스트리밍 된 HTTP 본문을 재 시도 할 수 없음"

그러나 그것은 나를 너무 멀리하지 않았습니다. 온 샘플 OkHttp GitHub의의의 repo는 중 인증 기반의 샘플을 기능하지 않았다. 누구든지 나를 올바른 방향으로 안내하는 요점이나 다른 코드 샘플이 있습니까? 도와 주셔서 감사합니다!


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

}

업데이트 된 코드는 다음과 같습니다.

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가 지적한대로 :

앞서 언급 한 솔루션에는 한 가지 단점이 있습니다. 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();

앞서 언급 한 솔루션에는 한 가지 단점이 있습니다. httpClient는 401 응답을받은 후에 만 ​​인증 헤더를 추가합니다. api-server와의 통신은 다음과 같습니다.enter image description here

모든 요청에 ​​대해 기본 인증을 사용해야하는 경우 각 요청에 인증 헤더를 추가하거나 다음과 같은 래퍼 메서드를 사용하는 것이 좋습니다.

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();
}

기본 64 인증을 사용하는 Okhttp3

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() {
...

Someone asked for a Kotlin version of the interceptor. Here is what I came up with and it works great:

        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()

All answers are good but no one said, that for some requests content-type is required, you should add a content-type to your request like this:

Request request = new Request.Builder()
        .url(url)
        .addHeader("content-type", "application/json") 
        .post(body)
        .build();

If you don't add it, you will get Unauthorized message and you will waste a lot of time to fix it.


I noticed on Android with some server APIs like django you should add a word in token

Request request = new Request.Builder()
    .url(theUrl)
    .header("Authorization", "Token 6utt8gglitylhylhlfkghriyiuy4fv76876d68")
    .build();

, where that problematic word is that "Token ". Overall you should carefully see rules of those specific server APIs about how to compose requests.


In OkHttp3, you set the authorization on the OkHttpClient itself by adding the authenticator() method. After your original calls come back with the 401 response, the authenticator() adds the Authorization header

 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();

Although it's more secure, if you don't want to spam the server with all the 401 requests in the first place, you can use something called preauthentication, where you send the Authorization header to begin with on your requests

String credentials = Credentials.basic("username", "password");
Request httpRequest = new Request.Builder()
                 .url("some/url")
                 .header("content-type", "application/json") 
                 .header(HttpHeaders.AUTHORIZATION, credentials)
                 .build();

In my case it only worked when I integrated authorization into the header (OkHttp Version 4.0.1):

Request request = new Request.Builder()
    .url("www.url.com/api")
    .addHeader("Authorization", Credentials.basic("username", "password"))
    .build();

Request response = client.newCall(request).execute();

This is a snippet for 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(); 

Do a request now. Basic auth will go as client already has that.

    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());

                }
            });

참고URL : https://stackoverflow.com/questions/22490057/android-okhttp-with-basic-authentication

반응형