Retrofit POST request w/ Basic HTTP Authentication: “Cannot retry streamed HTTP body”

大憨熊 提交于 2019-11-29 21:07:55

Your best bet is to provide your credentials to Retrofit via a RequestInterceptor instead of OkHttp's OkAuthenticator. That interface works best when the request can be retried, but in your case we've already thrown out the post body by the time we find out that's necessary.

You can continue to use OkAuthenticator's Credential class which can encode your username and password in the required format. The header name you want is Authorization.

Thanks, Jesse.

Just in case it helps, here is the code I did for Basic auth.

First, the init in MyApplication class:

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

And then the 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;
    }
}

Extending Naren's answer:

You build auth String like this:

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

And then you pass basicAuth to service as authorization.

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

For basic authorization you can provide a header like:

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

If you are doing this with the latest version of Retrofit / OkHttp, the current set of solutions aren't sufficient. Retrofit no longer offers a RequestInterceptor, so you need to use OkHttp's interceptors to accomplish a similar task:

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

You'd need to add the interceptor to your OkHttp Client:

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

I didn't test the above code, so some slight modifications may need to be made. I program in Kotlin for Android now-a-days.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!