【问题标题】:NTLM Authentication with HttpURLConnection使用 HttpURLConnection 进行 NTLM 身份验证
【发布时间】:2015-10-06 11:28:15
【问题描述】:

有没有办法使用HttpURLConnection 实现 NTLM 身份验证?目前我已经使用DefaultHttpClientJCIFSEngine 实现了它的身份验证方案。 (我的灵感是:Android: NTLM Authentication, ksoap, and persistent connections

但是自从 Android 6 Apache HTTP Client Removal 以来,我一直在寻找解决方案,除了在 app gradle 文件中添加 useLibrary 'org.apache.http.legacy' 之外,因为我想改用 HttpURLConnection 类来改进我的代码。正如文档所述,此 API 更高效,因为它通过透明压缩和响应缓存减少了网络使用量,并将功耗降至最低。

【问题讨论】:

  • 你看过 OkHTTP 和 Retrofit 吗?
  • 不,我会看看它们是否适合我的情况。

标签: android ntlm


【解决方案1】:

只有添加库jcifs,HttpURLConnection 才能与 NTLM 一起使用。

此示例适用于最新的jcifs-1.3.18

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;

import org.apache.http.impl.auth.NTLMEngineException;

public class TestNTLMConnection {
    public static void main(String[] args) throws UnknownHostException, IOException, NTLMEngineException {
        // Method 1 : authentication in URL
        jcifs.Config.registerSmbURLHandler();
        URL urlRequest = new URL("http://domain%5Cuser:pass@127.0.0.1/");

        // or Method 2 : authentication via System.setProperty()
        // System.setProperty("http.auth.ntlm.domain", "domain");
        // System.setProperty("jcifs.smb.client.domain", "domain");
        // System.setProperty("jcifs.smb.client.username", "user");
        // System.setProperty("jcifs.smb.client.password", "pass");
        // Not verified // System.setProperty("jcifs.netbios.hostname", "host");
        // System.setProperty("java.protocol.handler.pkgs", "jcifs");
        // URL urlRequest = new URL("http://127.0.0.1:8180/simulate_get.php");

        HttpURLConnection conn = (HttpURLConnection) urlRequest.openConnection();

        StringBuilder response = new StringBuilder();

        try {
            InputStream stream = conn.getInputStream();
            BufferedReader in = new BufferedReader(new InputStreamReader(stream));

            String str = "";
            while ((str = in.readLine()) != null) {
                response.append(str);
            }
            in.close();   

            System.out.println(response);
        } catch(IOException err) {
            System.out.println(err);
        } finally {
            Map<String, String> msgResponse = new HashMap<String, String>();

            for (int i = 0;; i++) {
                String headerName = conn.getHeaderFieldKey(i);
                String headerValue = conn.getHeaderField(i);
                if (headerName == null && headerValue == null) {
                    break;
                }
                msgResponse.put(headerName == null ? "Method" : headerName, headerValue);
            }

            System.out.println(msgResponse);
        }
    }
}

警告:jcifs 会忽略您在库中定义的 connectTimeoutreadTimeout,这就是当主机没有响应时连接需要很长时间才能中断的原因。使用我在this SO thread 中描述的代码来避免这个错误。

【讨论】:

  • 我不得不使用 jcifs.Config.setProperty() 而不是 System.setProperty()。似乎只有在文件 jcifs.properties 存在或直接调用方法 Config.load() 或 Config.setProperties() 之一时才使用 System.setProperty() 设置的属性。
  • 由于没有设置 Content-Length 标头,服务器响应 411 (Length Required) 也是一个问题。我必须调用 conn.setRequestProperty("Content-Length", "0") 并使用 -Dsun.net.http.allowRestrictedHeaders=true 运行 JVM。我想这只是某些 Java 版本和某些服务器的问题。与问题不严格相关,但我猜其他阅读此内容的人可能会遇到同样的问题。
  • 另外提示:如果没有域,必须设置为"",因为默认为null,会导致jcifs内部出现NullPointerException。
猜你喜欢
  • 2018-04-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多