简体   繁体   English

带有Retrofit2的基本身份验证用于Cloudinary

[英]Basic authentication with retrofit2 for cloudinary

Im trying to authenticate to Cloudinary API service using the below code but i get 401 unauthorized error, it expects credentials in this format https://API_KEY:API_SECRET@ ..., when i substitute with actual values it works great with browser/postman but fails with retrofit2, below is my code. 我试图使用下面的代码向Cloudinary API服务进行身份验证,但是我收到401未经授权的错误,它期望使用这种格式的凭据https:// API_KEY:API_SECRET @ ...,当我用实际值替换时,它可以很好地与浏览器/邮递员一起使用但是retrofit2失败,下面是我的代码。

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

}

Any help fixing the error will be highly appreciated, not sure if need custom converter. 修复错误的任何帮助将不胜感激,不确定是否需要自定义转换器。 thanks 谢谢

***** Edit****** *****编辑******

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

I was able to fix the issue with adding authenticator to the builder. 我可以通过向构建器添加身份验证器来解决此问题。

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

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

thanks for all your help. 感谢你的帮助。

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

//you can results before returing intercept //您可以在恢复拦截之前执行结果

The answer provided by leafNext will work but will cause every request to be sent twice - The authenticator only kicks in if the server responds with 401. You send the request, get 401 and then send it again with proper credentials. leafNext提供的答案将起作用,但将导致每个请求发送两次-身份验证器仅在服务器以401响应时启动。您发送请求,获取401,然后使用适当的凭据再次发送。

The correct solution is to provide the credentials from the get go, using the interceptor. 正确的解决方案是使用拦截器从一开始就提供凭据。 It's similar to what you tried to do originally, but you got the syntax wrong. 它与您最初尝试执行的操作类似,但是语法错误。 The expected format is 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());
    }
});

Where credentials should follow the basic authentication protocol: Assuming the Api key is key and the secret is secret , you base64-encode the expression key:secret and prefix it with Basic . credentials应遵循基本身份验证协议:假设Api密钥是key ,秘密是secret ,则对表达式key:secret进行base64编码,并在其前面加上Basic In this example the value of credentials should end up like so: 在此示例中, credentials的值应最终如下所示:

Basic a2V5OnNlY3JldA==


Edit - Added a fully working independent code bit to verify basic auth is working for okhttp (and thus with retrofit when using okhttp): 编辑-添加了一个完全正常工作的独立代码位,以验证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
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM