bearer authenticator android http-authentication retrofit okhttp

android - bearer - retrofit authenticator



Reorganizar la solicitud POST con autenticación HTTP básica: "No se puede reintentar el cuerpo HTTP transmitido" (5)

Extendiendo la respuesta de Naren:

Construyes una String autenticación de esta manera:

String basicAuth = "Basic " + Base64.encodeToString(String.format("%s:%s", "your_user_name", "your_password").getBytes(), Base64.NO_WRAP);

Y luego pasas basicAuth al servicio como authorization .

@GET("/user") void getUser(@Header("Authorization") String authorization, Callback<User> callback)

Estoy usando Retrofit para hacer una solicitud POST básica, y estoy proporcionando un @Body básico para la solicitud.

@POST("/rest/v1/auth/login") LoginResponse login(@Body LoginRequest loginRequest);

Cuando estoy creando la interfaz para Retrofit, estoy proporcionando mi propio OkHttpClient personalizado, y todo lo que le estoy haciendo es agregar mi propia autenticación personalizada:

@Provides @Singleton public Client providesClient() { OkHttpClient httpClient = new OkHttpClient(); httpClient.setAuthenticator(new OkAuthenticator() { @Override public Credential authenticate(Proxy proxy, URL url, List<Challenge> challenges) throws IOException { return getCredential(); } @Override public Credential authenticateProxy(Proxy proxy, URL url, List<Challenge> challenges) throws IOException { return getCredential(); } }); return new OkClient(httpClient); }

Esto funciona muy bien cuando envío solicitudes directamente con OKHttp, y otras solicitudes GET con actualización, pero cuando uso la modificación para hacer una solicitud POST obtengo el siguiente error:

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)

He jugado un poco con eso. Si elimino la autenticación y apunto a un servidor que no requiere la autenticación, entonces funciona bien.

  1. Así que debo estar enviando la información.
  2. Obtención de la solicitud de desafío de autenticación.
  3. Respondiendo a la solicitud de desafío.
  4. Intento volver a enviar la solicitud y, a continuación, se produce el error.

No estoy seguro de cómo solucionar esto. Cualquier ayuda sería maravillosa.


Gracias, Jesse.

Por si acaso ayuda, aquí está el código que hice para la autenticación básica.

Primero, el init en la clase MyApplication :

ApiRequestInterceptor requestInterceptor = new ApiRequestInterceptor(); requestInterceptor.setUser(user); // I pass the user from my model ApiService apiService = new RestAdapter.Builder() .setRequestInterceptor(requestInterceptor) .setServer(Constants.API_BASE_URL) .setClient(new OkClient()) // The default client didn''t handle well responses like 401 .build() .create(ApiService.class);

Y luego el ApiRequestInterceptor :

import android.util.Base64; import retrofit.RequestInterceptor; /** * Interceptor used to authorize requests. */ public class ApiRequestInterceptor implements RequestInterceptor { private User user; @Override public void intercept(RequestFacade requestFacade) { if (user != null) { final String authorizationValue = encodeCredentialsForBasicAuthorization(); requestFacade.addHeader("Authorization", authorizationValue); } } private String encodeCredentialsForBasicAuthorization() { final String userAndPassword = user.getUsername() + ":" + user.getPassword(); return "Basic " + Base64.encodeToString(userAndPassword.getBytes(), Base64.NO_WRAP); } public User getUser() { return user; } public void setUser(User user) { this.user = user; } }


Para la autorización básica puede proporcionar un encabezado como:

@GET("/user") void getUser(@Header("Authorization") String authorization, Callback<User> callback)


Si está haciendo esto con la última versión de Retrofit / OkHttp, el conjunto actual de soluciones no es suficiente. Retrofit ya no ofrece un RequestInterceptor, por lo que necesita usar los interceptores de OkHttp para realizar una tarea similar:

Crea tu interceptor:

public class HttpAuthInterceptor implements Interceptor { private String httpUsername; private String httpPassword; public HttpAuthInterceptor(String httpUsername, String httpPassword) { this.httpUsername = httpUsername; this.httpPassword = httpPassword; } @Override public Response intercept(Chain chain) throws IOException { Request newRequest = chain.request().newBuilder() .addHeader("Authorization", getAuthorizationValue()) .build(); return chain.proceed(newRequest); } private String getAuthorizationValue() { final String userAndPassword = "httpUsername" + ":" + httpPassword; return "Basic " + Base64.encodeToString(userAndPassword.getBytes(), Base64.NO_WRAP); } }

Necesitaría agregar el interceptor a su cliente OkHttp:

// Create your client OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(new HttpAuthInterceptor("httpUsername", "httpPassword")) .build(); // Build Retrofit with your client Retrofit retrofit = new Retrofit.Builder() .client(client) .build(); // Create and use your service that now authenticates each request. YourRetrofitService service = retrofit.create(YourRetrofitService.class);

No probé el código anterior, por lo que es posible que deban realizarse algunas modificaciones leves. Programo en Kotlin para Android hoy en día.


Su mejor apuesta es proporcionar sus credenciales para la modificación a través de un RequestInterceptor lugar del OkAuthenticator de OkAuthenticator . Esa interfaz funciona mejor cuando se puede volver a intentar la solicitud, pero en su caso ya hemos eliminado el cuerpo de la publicación cuando nos damos cuenta de que es necesario.

Puede continuar usando la clase de credencial de OkAuthenticator que puede codificar su nombre de usuario y contraseña en el formato requerido. El nombre del encabezado que desea es Authorization .