Android OkHttp con autenticación básica

5 minutos de lectura

avatar de usuario
Kerr

Estoy usando la biblioteca OkHttp para un nuevo proyecto y estoy impresionado con su facilidad de uso. Ahora necesito usar la autenticación básica. Desafortunadamente, hay una escasez de código de muestra que funcione. Estoy buscando un ejemplo de cómo pasar las credenciales de nombre de usuario/contraseña a OkAuthenticator cuando se encuentra un encabezado HTTP 401. Vi esta respuesta:

Actualización de solicitud POST con autenticación HTTP básica: “No se puede volver a intentar el cuerpo HTTP transmitido”

pero no me llevó demasiado lejos. Las muestras en el OkHttp repositorio de github tampoco presentó una muestra basada en autenticación. ¿Alguien tiene una esencia u otra muestra de código para orientarme en la dirección correcta? ¡Gracias por tu ayuda!

Código de actualización para 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;
}

}

  • Esto tiene que ir a la cima 🙂

    – Ionut

    17 de febrero de 2016 a las 17:12

  • ¡Gracias por el seguimiento!

    – Kerr

    25 de febrero de 2016 a las 21:26

  • ¿Cómo conectas esto dentro de un método? Sigo recibiendo un error del compilador: “no se puede aplicar el autenticador en OKHttpClient (okhttp3.Authenticator anónimo)”

    – Mike6679

    1 dic 2016 a las 20:00

  • ahhh ya veo, Authenticator es una interfaz.

    – Mike6679

    1 de diciembre de 2016 a las 20:46


  • @nuss +1 para el never give up comenta XD

    – Banana

    9 dic 2018 a las 10:15

Como señaló @agamov:

La solución antes mencionada tiene un inconveniente: httpClient agrega encabezados de autorización solo después de recibir la respuesta 401

@agamov propuso entonces agregar “manualmente” encabezados de autenticación a cada solicitud, pero hay una mejor solución: usar un 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);
    }

}

Luego, simplemente agregue el interceptor a un cliente OkHttp que utilizará para realizar todas sus solicitudes autenticadas:

OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(new BasicAuthInterceptor(username, password))
    .build();

  • Esto funciona perfectamente. Exactamente lo que necesitaba para evitar múltiples llamadas al 401. Mi API requiere que todas las llamadas sean autenticadas.

    – Hackmodford

    10 de agosto de 2016 a las 19:44

  • ¿Hay una versión kotlin de este Interceptor? @Alphaaaa

    – stebak

    21 de marzo de 2018 a las 8:46

  • @KBJ Este interceptor funciona con OkHttp, que es una biblioteca de Java. Las bibliotecas HTTP generalmente permiten trabajar con interceptores, por lo que si encuentra uno para Kotlin, puede implementar algo similar a esto 🙂

    – alfaaaa

    21 de marzo de 2018 a las 18:28

  • En mi opinión, podemos usar la interfaz Interceptor si todos los puntos finales requieren autenticación o si tenemos una lista finita de puntos finales con este requisito. Cuando no sabemos exactamente qué puntos finales requieren autenticación, podemos usar la interfaz del Autenticador. Cuando lo necesitemos, podemos mezclar estos 2 enfoques para evitar solicitudes dobles (401 -> 200) y evitar agregar un encabezado de Autorización en lugares donde no lo necesitamos (problema de seguridad).

    – ultraón

    17 dic 2018 a las 23:32

  • Esta no es la manera más eficiente de hacer esto. La razón es que el código interceptor se ejecutará cada vez que hagamos una solicitud al servidor. Entonces, de esta manera estaremos creando un request.newBuilder() cada vez Mientras que, el autenticador debe agregarse solo a los del cliente.

    – Shriharsh Pathak

    14 de diciembre de 2021 a las 8:17

Aquí está el código actualizado:

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

  • Esta es la respuesta actual usando OkHttp 2.x

    – Ian Timmis

    03/12/2016 a las 20:39

avatar de usuario
Jesse Wilson

Intenta usar OkAutenticador:

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

ACTUALIZAR:

Renombrado a Autenticador

La solución antes mencionada tiene un inconveniente: httpClient agrega encabezados de autorización solo después de recibir la respuesta 401. Así es como se veía mi comunicación con api-server:
ingrese la descripción de la imagen aquí

Si necesita usar autenticación básica para cada solicitud, mejor agregue sus encabezados de autenticación a cada solicitud o use un método de envoltura como este:

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

  • Buena atrapada. Escribí una respuesta mejorada que se basa en la tuya: stackoverflow.com/a/36056355/2091700

    – alfaaaa

    17 de marzo de 2016 a las 9:27

avatar de usuario
guardagujas

Okhttp3 con autenticación 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() {
...

  • Buena atrapada. Escribí una respuesta mejorada que se basa en la tuya: stackoverflow.com/a/36056355/2091700

    – alfaaaa

    17 de marzo de 2016 a las 9:27

avatar de usuario
rico gabrielli

Alguien pidió una versión de Kotlin del interceptor. Esto es lo que se me ocurrió y funciona muy bien:

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

  • Es bueno ver que siguen llegando nuevas respuestas a mi pregunta a medida que evolucionan los lenguajes y las técnicas.

    – Kerr

    27 de abril de 2018 a las 12:57

  • ¡Interceptor es la herramienta equivocada para hacer eso! Utilice el Autenticador en su lugar. Te evitarás muchos problemas.

    – Dawid Hyży

    13 de marzo de 2019 a las 9:30

¿Ha sido útil esta solución?