Java 7在Linux上使用NTLM身份验证的URLConnection出现错误

11
我正在尝试打开一个使用NTLM身份验证方案保护的URL的http连接。当我们使用Java 6时,这段代码已经正确工作了2年。我编写了一个小的Java程序来访问特定的URL,以使测试用例尽可能简单。
问题在于,我无法使程序在Linux上工作,并且在使用JDK 7版本时也是如此。Java尝试20次访问URL,然后我收到一个错误消息,告诉我服务器重定向太多次了。它在Linux和JDK 6上运行良好,在Windows 7上使用JDK 6或7也可以正常工作。
我检查并尝试了列在这里(以及其他很多)的解决方案:Getting "java.net.ProtocolException: Server redirected too many times" Error。它没有起作用。我还必须补充一点,当从浏览器访问URL时,我可以看到没有涉及到任何cookie。
以下是我尝试过的操作系统/ Java版本的详细信息:
成功:
  • Windows 7: Java(TM) SE Runtime Environment (版本 1.7.0_15-b03) (64 位)
  • Windows 7: Java(TM) SE Runtime Environment (版本 1.7.0_10-b18) (64 位)
  • Windows 7: Java(TM) SE Runtime Environment (版本 1.6.0_33-b04) (64 位)
  • Redhat enterprise linux 6.4: Java(TM) SE Runtime Environment (版本 1.6.0_33-b04) (64 位)

失败:

  • Redhat enterprise linux 6.4: Java(TM) SE Runtime Environment (版本 1.7.0-b147) (64 位)
  • Redhat enterprise linux 6.4: Java(TM) SE Runtime Environment (版本 1.7.0_05-b06) (64 位)
  • Redhat enterprise linux 6.4: Java(TM) SE Runtime Environment (版本 1.7.0_13-b20) (64 位)
  • Redhat enterprise linux 6.4: Java(TM) SE Runtime Environment (版本 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”替换我尝试强制的域,但由于用户存在于服务器上而不是服务器的域上,因此会失败。
我不确定该怎么处理这个问题。

你使用的是哪个Java 7版本?我在处理https、身份验证或安全性时遇到了一些问题。我开发的一个应用程序只能在Java 1.7_02(Java 7.2)上运行,但当我使用最新版本时它就无法工作了。Oracle在最新版本中进行了一些修改,特别是在安全策略方面。尝试使用旧版本的Java 7,比如1.7_02。 - Marcelo Tataje
我刚刚尝试了在Linux上使用Java jdk版本1.7.0_02 64位,但它没有起作用,结果相同。 - Yanick
同样的,对于 Linux(64 位)来说,1.7.0_25-b15 也是虚假的。 - Carlo Pellegrini
尝试使用1.7.0_40版本
  • 64位在Linux上失败
  • 32位在Solaris上失败
  • 64位在Windows7上成功
- Patrick Roumanoff
5个回答

4
我是一位帮助翻译的助手。
我在Client.java类中更改了代码,并与com.sun.security.ntlm包的其余部分一起重新编译,然后创建了一个名为rt_fix.jar的jar文件,其中包含该特定包的类。接下来,我使用Java启动选项强制加载我的jar文件,以便在内部rt.jar之前加载它。
-Xbootclasspath/p:/path_to_jar/rt_fix.jar 我不喜欢这个解决方案,但它起作用了。
以下是我在Client.java中更改的代码,在type3方法中:
Before:
    if (domainFromServer != null) {
        domain = domainFromServer;
    }

之后:

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

当发送NTLM认证的第三部分时,它阻止Java改变我尝试进行身份验证的域并将其替换为从服务器接收到的域。我尝试进行身份验证的域实际上是服务器名称,因为用户帐户是本地的。


你向Oracle报告了这个漏洞吗? - Armand
错误报告:http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8058419。 仍被标记为开放状态。 - Adam C.

3

我曾经遇到过同样的问题,并通过在用户名中包含域名来解决它:

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

3

正确的做法是:

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

2

0

这个错误的另一个可能原因是:域用户被阻止了。

Scheme:ntlm
Scheme:ntlm
java.net.ProtocolException: Server redirected too many  times (20)

在尝试使用超过域策略允许的密码重试次数登录时,我不小心阻止了我的域用户,并遇到了同样的错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接