okHttp 3.x身份验证器未被调用

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了okHttp 3.x身份验证器未被调用相关的知识,希望对你有一定的参考价值。

我需要通过需要身份验证的代理发出请求。

    public class WebClient 

    private final OkHttpClient httpClient;
    private static WebClient webClient;

    private WebClient() 
        OkHttpClient.Builder builder = new OkHttpClient.Builder();

        if (Configurator.getInstance().useProxy()) 
            builder.proxySelector(new CustomProxySelector());
            builder.authenticator((Route route, Response response) -> 
                String credential = Credentials.basic("MYUSER", "MYPSW");
                return response.request().newBuilder().header("Authorization", credential).build();
            );
         else
            builder.proxy(Proxy.NO_PROXY);

        httpClient = builder
                .connectTimeout(60, TimeUnit.SECONDS)
                .writeTimeout(60, TimeUnit.SECONDS)
                .readTimeout(60, TimeUnit.SECONDS)
                .build();
    

但是使用调试器我看到验证器方法永远不会被调用,我收到407作为任何请求的响应。

但是,当我使用HttpURLConnection与Authenticator.setDefault时,它工作正常,我可以使用我的代理身份验证:

public boolean hasInternetConnection() throws IOException 
    Request httpRequest = new Request.Builder().url("http://www.google.com/").build();
    // This fails with 407
    Response httpResponse = httpClient.newCall(httpRequest).execute();

    java.net.Authenticator authenticator = new java.net.Authenticator() 
        public PasswordAuthentication getPasswordAuthentication() 
            return (new PasswordAuthentication("MYUSER", "MYPSW".toCharArray()));
        
    ;

    java.net.Authenticator.setDefault(authenticator);

    URL obj = new URL("http://www.google.com/");
    HttpURLConnection con = (HttpURLConnection) obj.openConnection();
    con.setRequestMethod("GET");
    // This works with 200
    int responseCode = con.getResponseCode();

    return false;

所以我认为问题是:为什么没有调用OkHttpClient.Builder.authenticator方法?

答案

正如Gimby指出的那样,其中一个问题是我说错了方法。让我困惑的是,有时候proxyAuthenticator没有被调用,我试图找出原因。

我开发的应用程序需要访问我的工作网络内外的资源。因此,当我需要外部访问时,我必须使用具有身份验证的代理。它的工作原理如下:

  1. 向互联网主机发出请求;
  2. ProxySelector决定HTTP客户端应该使用代理来处理此请求,因为它是一个Internet主机;
  3. 由于设置了代理,因此调用ProxyAuthenticator以在请求中发送授权标头。

但是,当向内部主机发出请求时,ProxySelector会确定不需要使用代理。因此,ProxyAuthenticator不会被调用,因为没有活动代理。

以下是我对任何感兴趣的人的实施:

Web client.Java

    public class WebClient 

    private final OkHttpClient httpClient;
    private static WebClient webClient;

    private WebClient() 
        OkHttpClient.Builder builder = new OkHttpClient.Builder();

        if (Configurator.getInstance().useProxy()) 
            CodeUtils.setProxy();
            builder.proxySelector(new CustomProxySelector());
            builder.proxyAuthenticator(new CustomProxyAuthenticator());
         else 
            builder.proxy(Proxy.NO_PROXY);
            CodeUtils.removeProxy();
        

        httpClient = builder
                .connectTimeout(10, TimeUnit.SECONDS)
                .writeTimeout(10, TimeUnit.SECONDS)
                .readTimeout(10, TimeUnit.SECONDS)
                .build();
    

    public static WebClient getInstance() 
        return webClient != null ? webClient : (webClient = new WebClient());
    

    public static void reload() 
        webClient = null;
    

    public String doGet(String url) throws IOException 
        Request httpRequest = new Request.Builder().url(url).build();
        Response httpResponse = httpClient.newCall(httpRequest).execute();

        if (httpResponse.code() != 200) 
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("success", false);
            jsonObject.put("msg", httpResponse.body().string());
            jsonObject.put("httpCode", httpResponse.code());
            return jsonObject.toString();
        

        return httpResponse.body().string();
    

    public String doPost(String url, JSONObject body) throws IOException 
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), body.toString());
        Request request = new Request.Builder()
                .header("Accept", "application/json")
                .header("Content-type", "application/json; charset=UTF-8")
                .url(url)
                .post(requestBody).build();
        Response response = httpClient.newCall(request).execute();
        return response.body().string();
    

custom proxy authenticator.Java

public class CustomProxyAuthenticator implements Authenticator 

    @Override
    public Request authenticate(Route route, Response response) throws IOException 
        String username = Configurator.getInstance().getProxyUser();
        String password = Configurator.getInstance().getProxyPassword();

        String credential = Credentials.basic(username, password);
        return response.request().newBuilder()
                .header("Proxy-Authorization", credential)
                .build();
    

custom proxy selector.Java

public class CustomProxySelector extends ProxySelector 

    private Configurator configurator = Configurator.getInstance();
    private List<String> nonProxyHosts = Arrays.asList(configurator.getNonProxyHosts().split("\\|"));
    private String proxyHost = configurator.getProxyHost();
    private int proxyPort = configurator.getProxyPort();

    @Override
    public List<Proxy> select(URI uri) 
        final List<Proxy> proxyList = new ArrayList<>(1);
        String host = uri.getHost();

        if (host.startsWith("127.0.0.1") || nonProxyHosts.contains(host))
            proxyList.add(Proxy.NO_PROXY);
        else
            proxyList.add(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort)));

        return proxyList;
    

    @Override
    public void connectFailed(URI arg0, SocketAddress arg1, IOException arg2) 
    

以上是关于okHttp 3.x身份验证器未被调用的主要内容,如果未能解决你的问题,请参考以下文章

Okhttp 3.x 动态添加/删除验证器/拦截器

在 Okhttp 中处理身份验证

带有身份验证的 okhttp 发布请求

具有基本身份验证的 Android OkHttp

OkHttp + Retrofit libssl 以两种方式崩溃 SSL 身份验证

Play 2.3.x 中的身份验证