问题 使用ntlm身份验证时出现Java URLConnection错误,但仅限Linux和Java 7


我正在尝试打开与NTLM身份验证方案保护的URL的http连接。当我们使用Java 6时,这段代码已经正常工作了2年。我写了一个小的java程序,它访问该特定的url,使测试用例尽可能简单。

问题是我无法使程序在Linux上工作,并且在使用JDK 7版本时.Java会尝试20次访问URL,然后我收到错误,告诉我服务器重定向次数太多。它适用于linux和JDK 6,以及带有JDK 6或7的Windows 7。

我检查并尝试了此处列出的解决方案(和许多其他人): 获取“java.net.ProtocolException:服务器重定向次数太多”错误。它没用。我还必须补充一点,当从浏览器访问URL时,我可以看到没有涉及cookie。

这是我尝试过的os / java版本的确切细节:

成功:

  • Windows 7:Java(TM)SE运行时环境(版本1.7.0_15-b03)(64位)
  • Windows 7:Java(TM)SE运行时环境(版本1.7.0_10-b18)(64位)
  • Windows 7:Java(TM)SE运行时环境(版本1.6.0_33-b04)(64位)
  • Redhat enterprise linux 6.4:Java(TM)SE运行环境(版本1.6.0_33-b04)(64位)

失败:

  • Redhat enterprise linux 6.4:Java(TM)SE运行时环境(版本1.7.0-b147)(64位)
  • Redhat enterprise linux 6.4:Java(TM)SE运行环境(版本1.7.0_05-b06)(64位)
  • Redhat enterprise linux 6.4:Java(TM)SE运行时环境(版本1.7.0_13-b20)(64位)
  • Redhat enterprise linux 6.4:Java(TM)SE运行时环境(版本1.7.0_15-b03)(64位)

当程序工作时,我看到使用的身份验证方法和我尝试下载的文档作为输出:

Scheme:Negotiate
Scheme:ntlm
.... document content ....
Done

当它失败时,我有以下输出:

Scheme:Negotiate
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
java.net.ProtocolException: Server redirected too many  times (20)
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1635)
        at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
        at TestWs.testWs(TestWs.java:67)
        at TestWs.main(TestWs.java:20)

这是该程序的源代码:

package com.test;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Authenticator;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.PasswordAuthentication;
import java.net.URL;
import java.net.URLConnection;

public class TestWs {

    public static void main(String[] args) throws Exception {
        new TestWs().testWs();
    }

    public void testWs() {
        try {
            CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));
            Authenticator.setDefault(new MyAuthenticator("username", "password"));

            URL url = new URL("https://someurlprotectedbyntlmauthentication.com");
            URLConnection connection = url.openConnection();
            InputStream is = connection.getInputStream();
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            while (true) {
                String s = br.readLine();
                if (s == null)
                    break;
                System.out.println(s);
            }
            System.out.println("Done");
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

class MyAuthenticator extends Authenticator {
    private String httpUsername;
    private String httpPassword;

    public MyAuthenticator(String httpUsername, String httpPassword) {
        this.httpUsername = httpUsername;
        this.httpPassword = httpPassword;
    }

    @Override
    protected PasswordAuthentication getPasswordAuthentication() {
        System.out.println("Scheme:" + getRequestingScheme());
        return new PasswordAuthentication(httpUsername, httpPassword.toCharArray());
    }
}

任何帮助将不胜感激。

更新:

经过一些调查后,我发现如果我使用域用户,身份验证可以正常工作,但如果我使用本地用户则不会。

来自JDK 7的代码导致我遇到麻烦(类com.sun.security.ntlm.Client):

public byte[] type3(byte[] type2, byte[] nonce) throws NTLMException {
if (type2 == null || (v != Version.NTLM && nonce == null)) {
throw new NullPointerException("type2 and nonce cannot be null");
}
debug("NTLM Client: Type 2 received\n");
debug(type2);
Reader r = new Reader(type2);
byte[] challenge = r.readBytes(24, 8);
int inputFlags = r.readInt(20);
boolean unicode = (inputFlags & 1) == 1;
String domainFromServer = r.readSecurityBuffer(12, unicode);
if (domainFromServer != null) {
domain = domainFromServer;
}

因此,由于服务器在域中被加入,它将作为NTLM协议的一部分发送回客户端。 Java每次都会替换我试图通过变量“domainFromServer”强制执行的域,并且由于用户存在于服务器而不是服务器的域上而失败。

我不知道该怎么做。


9057
2018-02-27 22:47


起源

您使用的java 7版本是什么?使用https或身份验证或安全性时,我遇到了一些问题。我开发的应用程序只使用java 1.7_02(Java 7.2),但是当我使用最新版本时它不再起作用了。 Oracle在最新版本中对安全策略进行了一些修改。尝试使用旧版本的java 7,如1.7_02。 - Marcelo Tataje
我只是尝试使用Java jdk版本1.7.0_02 64位for linux,它没有用,结果相同。 - Yanick
对于Linux(64位)也是假的1.7.0_25-b15 - Carlo Pellegrini
尝试1.7.0_40 - 在Linux上64位失败 - 在solaris上32位失败 - 在windows7中64位成功 - Patrick Roumanoff


答案:


我更改了Client.java类中的代码,并将其与com.sun.security.ntlm包的其余部分一起重新编译,然后创建了一个名为rt_fix.jar的jar,其中包含该特定包的类。然后我使用java启动选项强制它在内部rt.jar之前加载我的jar。

-Xbootclasspath / P:/path_to_jar/rt_fix.jar

我不喜欢这个解决方案,但它有效。

这是我在Client.java中更改的代码,方法类型为3:

之前:

    if (domainFromServer != null) {
        domain = domainFromServer;
    }

之后:

    if (domainFromServer != null) {
        //domain = domainFromServer;
    }

它阻止Java在发送NTLM身份验证的第3部分时,使用从服务器收到的域更改我尝试进行身份验证的域。我尝试进行身份验证的域实际上是服务器的名称,因为用户帐户是本地的。


4
2018-03-01 20:04



你是英雄!谢谢你的回答。 - Patrick Roumanoff
您是否向Oracle报告了该错误? - Armand
错误报告: bugs.java.com/bugdatabase/view_bug.do?bug_id=8058419。仍然标记为开放。 - Adam C.


我遇到了同样的问题并通过指定包含在其中的域的用户名来解决它:

    Authenticator.setDefault(new Authenticator() {
        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(
                    System.getProperty("DOMAIN\\user"),
                    System.getProperty("password").toCharArray() ) ;
        }
    });

3
2017-08-02 06:07





这是正确的:

Authenticator.setDefault(new Authenticator() {
        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(
                    "DOMAIN\\user",
                    "password".toCharArray() ) ;
        }
    });

3
2018-01-27 16:36



解释为什么这样有效。 - Bleeding Fingers
这适用于Linux上的Java 7吗? - Suman


此问题已在JDK 9-ea中解决 http://bugs.java.com/view_bug.do?bug_id=7150092 并且该修复程序也被移植到JDK 8u40 http://bugs.java.com/view_bug.do?bug_id=8049690 


2
2017-12-07 14:24