【问题标题】:Basic authentication with retrofit2 for cloudinary用于 cloudinary 的改造 2 的基本身份验证
【发布时间】:2017-09-23 05:24:33
【问题描述】:

我尝试使用以下代码对 Cloudinary API 服务进行身份验证,但我收到 401 未经授权的错误,它需要这种格式的凭据 https://API_KEY:API_SECRET@...,当我用实际值替换它时,它适用于浏览器/邮递员但失败了使用改造2,下面是我的代码。

// create and initialize retrofit2 client

public static OkHttpClient getClient(){

HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(Level.BASIC);

OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request().newBuilder()
.addHeader("API_KEY","API_SECRET")
.addHeader("Accept","Application/JSON").build();

return chain.proceed(request);

}
})
.addInterceptor(interceptor)
.build();

return client;
}

private static Retrofit retrofit = null;

public static Retrofit getClient(String baseUrl){
if (retrofit == null){

retrofit = new Retrofit.Builder()
.client(getClient())
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}

// Interface with get methods to access image resources
public interface CloudinaryService {

@GET("resources/image")
Call<imageresponse> getImageResource();
}

// Util class to make requests

public class ApiUtils {
private static final String BASE_URL = "http://api.cloudinary.com/v...";
public static CloudinaryService getImageService(){
return RetrofitClient.getClient(BASE_URL)
.create(CloudinaryService.class);
}

}

任何解决错误的帮助将不胜感激,不确定是否需要自定义转换器。谢谢

***** 编辑******

public static String credentials = Credentials.basic(API_KEY,API_SECRET);

      OkHttpClient client = new OkHttpClient.Builder()

//                .authenticator(new Authenticator() {
//                    @Override
//                    public Request authenticate(Route route, Response response) throws IOException {
//
//                        return response.request().newBuilder().header("Authorization", credentials).build();
//                    }
//                })
                .addInterceptor(new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {

                        Request request = (chain.request().newBuilder()
                                .header("Accept","Application/JSON")
                                .header("Cache-Control", "public, max-age=" + 60)
                                .header("Authorization",credentials).build());


                        return chain.proceed(request);

                    }
                })
                .connectTimeout(10, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .addInterceptor(loggingInterceptor)
                .addInterceptor(provideOfflineCacheInterceptor())
                .addNetworkInterceptor(provideCacheInterceptor())
                .cache(getCache())
                .build();

        return client;
    }

【问题讨论】:

  • 您没有在此处使用身份验证,这解释了错误...您希望如何传递凭据?您是否尝试过搜索“改造基本身份验证”?
  • 您不会将该数据作为标头传递。 futurestud.io/tutorials/…
  • 它需要这种格式的凭据API_KEY:API_SECRET@...,。您能否对此进行更多解释,因为我看到您的 BASE_URL 不同。

标签: java android retrofit2 cloudinary


【解决方案1】:

我能够通过向构建器添加验证器来解决问题。

.authenticator(new Authenticator() {
                    @Override
                    public Request authenticate(Route route, Response response) throws IOException {

                        return response.request().newBuilder().header("Authorization", credentials).build();
                    }
                })

感谢您的帮助。

【讨论】:

    【解决方案2】:
            request = chain.request();
            builder = request.newBuilder();
    
            if (TextUtils.isEmpty(request.header(AUTH)) && UserPreference.getInstance().isSignin())
                builder.addHeader(AUTH, UserPreference.getInstance().getAccessToken());
    
            if (NetUtil.hasNetwork(GridInnApplication.getInstance()))
                builder.header(USER_AGENT, userAgent);
            else
                builder.cacheControl(CacheControl.FORCE_CACHE);
    
            request = builder.build();
            Response response = chain.proceed(request);
    
            if (NetUtil.hasNetwork(GridInnApplication.getInstance())) {
                String cacheControl = request.cacheControl().toString();
                return response.newBuilder()
                        .header(CACHE_CONTROL, cacheControl)
                        .removeHeader(PRAGMA)
                        .build();
            } else {
                return response.newBuilder()
                        .addHeader(CACHE_CONTROL, CACHE_CONTROL_ONLY_CACHED)
                        .removeHeader(PRAGMA)
                        .build();
            }
    

    //返回拦截前可以得到结果

    【讨论】:

      【解决方案3】:

      leafNext 提供的答案将起作用,但会导致每个请求被发送两次 - 只有当服务器以 401 响应时,身份验证器才会启动。您发送请求,获取 401,然后使用正确的凭据再次发送。

      正确的解决方案是使用拦截器从一开始就提供凭据。它与您最初尝试做的类似,但您的语法错误。预期格式为basic authentication.

      .addInterceptor(new Interceptor() {
          @Override
          public Response intercept(Interceptor.Chain chain) throws IOException {
              // Request customization: add request headers
              return chain.proceed(chain.request().newBuilder()
                  .header("Authorization", credentials).build());
          }
      });
      

      credentials 应遵循基本身份验证协议:假设 Api 密钥为 key,秘密为 secret,您对表达式 key:secret 进行 base64 编码,并在其前面加上 Basic。在这个例子中,credentials 的值应该是这样的:

      Basic a2V5OnNlY3JldA==


      编辑 - 添加了一个完全工作的独立代码位来验证基本身份验证是否适用于 okhttp(因此在使用 okhttp 时需要进行改造):

      public int testBasicAuth() throws IOException {
          OkHttpClient client = new OkHttpClient.Builder()
                  .addInterceptor(new Interceptor() {
                      @Override
                      public Response intercept(Chain chain) throws IOException {
                          Request request = (chain.request().newBuilder()
                                  .header("Authorization",okhttp3.Credentials.basic(KEY, SECRET)).build());
                          return chain.proceed(request);
                      }
                  }).build();
      
          Request request = new Request.Builder()
                  .url("https://api.cloudinary.com/v1_1/[cloud_name]/resources/image")
                  .build();
      
          int code = client.newCall(request).execute().code(); 
          return code; // 200
      }
      

      【讨论】:

      • 感谢您的评论,我在上面尝试了您的代码,但仍然返回 401,但可以与身份验证器一起使用,有什么线索吗?
      • 我阅读了其他博客,也记录了 OKhttp 调用我不确定你的两次身份验证理论是否正确,它似乎在第一次尝试时拦截了调用并进行了很好的身份验证。
      • this discussion。如cookbook 中所述,Authenticator 专门用于重试。 source 还显示 authenticate 仅在收到 401 响应后才被调用。不管怎样,你确认你的拦截器被激活了吗?你能发布更新的代码吗?
      • 是的,你是对的,但是当我通过拦截器传递授权标头时,我得到 401,我添加了最近的代码
      • 我编辑了我的答案,添加了一个绝对有效的代码位(用我的凭据测试)。使用您的云名称和凭据对其进行测试。如果这对我不起作用,我们将在聊天中继续。
      猜你喜欢
      • 1970-01-01
      • 2020-06-08
      • 2015-08-12
      • 2021-01-25
      • 1970-01-01
      • 2012-07-26
      • 2017-04-11
      • 2012-07-19
      • 1970-01-01
      相关资源
      最近更新 更多