ntlm 身份验证的 Java URLConnection 错误,但仅限于 Linux 和 Java 7

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15124019/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 18:33:36  来源:igfitidea点击:

Java URLConnection error with ntlm authentication, but only on Linux and only Java 7

javaauthenticationntlmurlconnection

提问by Yanick

I am trying to open an http connection to an url protected with the NTLM authentication scheme. This code has been working correctly for 2 year when we were on Java 6.I wrote a small java program which access that particular url to make the test case as simple as possible.

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

The problem is that I am unable to make the program work on linux and when using versions of the JDK 7. Java tries 20 times to access the URL and then I get an error telling me that the server redirected too many times. It works fine with linux and JDK 6, and in windows 7 with JDK 6 or 7.

问题是我无法让程序在 linux 上运行,并且在使用 JDK 7 版本时。Java 尝试了 20 次访问 URL,然后我收到一个错误,告诉我服务器重定向了太多次。它适用于 linux 和 JDK 6,适用于 JDK 6 或 7 的 Windows 7。

I checked and tried the solution listed here (and many others) : Getting "java.net.ProtocolException: Server redirected too many times" Error. It didn't work. I also have to add that when accessing the url from a browser, I can see that there are no cookies involved.

我检查并尝试了此处列出的解决方案(以及许多其他解决方案):获取“java.net.ProtocolException: Server redirected too many times”错误。它没有用。我还必须补充一点,当从浏览器访问 url 时,我可以看到没有涉及 cookie。

Here is the exact detail of the os/java versions that I have tried :

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

Success:

成功:

  • Windows 7: Java(TM) SE Runtime Environment (build 1.7.0_15-b03) (64 bit)
  • Windows 7: Java(TM) SE Runtime Environment (build 1.7.0_10-b18) (64 bit)
  • Windows 7: Java(TM) SE Runtime Environment (build 1.6.0_33-b04) (64 bit)
  • Redhat enterprise linux 6.4: Java(TM) SE Runtime Environment (build 1.6.0_33-b04) (64 bit)
  • 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 位)

Fail:

失败:

  • Redhat enterprise linux 6.4: Java(TM) SE Runtime Environment (build 1.7.0-b147) (64 bit)
  • Redhat enterprise linux 6.4: Java(TM) SE Runtime Environment (build 1.7.0_05-b06) (64 bit)
  • Redhat enterprise linux 6.4: Java(TM) SE Runtime Environment (build 1.7.0_13-b20) (64 bit)
  • Redhat enterprise linux 6.4: Java(TM) SE Runtime Environment (build 1.7.0_15-b03) (64 bit)
  • Redhat 企业 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 位)

When the program works, I see the authentication methods that were used and the document that I am trying to download as the output:

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

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

When it fails, I have the following output :

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

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)

Here is the source code of the program:

这是程序的源代码:

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

Any help would be greatly appreciated.

任何帮助将不胜感激。

UPDATE:

更新:

After some more investigation, I found that the authentication works if I use a domain user, but not if I use a local user.

经过更多调查,我发现如果我使用域用户,身份验证有效,但如果我使用本地用户,则无效。

This code from the JDK 7 causes me troubles (class com.sun.security.ntlm.Client) :

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

So, since the server is enroled in a domain, it sends back to the client it's domain as part of the NTLM protocol. Java replaces the domain that I'm trying to force by the variable "domainFromServer" everytime and it fails since the user exists on the server and not on the server's domain.

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

I don't know exactly what to do with that.

我不知道该怎么做。

采纳答案by Yanick

I changed code in the Client.java class, and recompiled it along with the rest of the com.sun.security.ntlm package, then I created a jar called rt_fix.jar which contains the classes of that particular package. Then I used a java startup option to force it to load my jar before the internal rt.jar.

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

-Xbootclasspath/p:/path_to_jar/rt_fix.jar

-Xbootclasspath/p:/path_to_jar/rt_fix.jar

I don't like this solution, but it worked.

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

Here is the code I changed in Client.java, in the method type3:

这是我在 Client.java 中更改的代码,在方法 type3 中:

Before :

前 :

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

After :

后 :

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

It stops Java from altering the domain I try to authenticate to with the one recieved from the server when sending the 3rd part of the NTLM authentication. The domain I was trying to authenticate to is in fact the name of the server because the user accounts are local.

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

回答by Maravilloso

I had the same problem and solved it just by specifying the user name with the domain included in it:

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

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

回答by user3241254

Correct is this:

正确的是这样的:

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

回答by Pallavi Sonal

This issue was resolved in JDK 9-ea with http://bugs.java.com/view_bug.do?bug_id=7150092and the fix was back-ported to JDK 8u40 also with the http://bugs.java.com/view_bug.do?bug_id=8049690

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

回答by Aleksey Timohin

One more possible cause for this error: domain user is blocked

此错误的另一个可能原因:域用户被阻止

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

I had the same error after I accidentally blocked my domain user, when tried to login with incorrect password more than it allowed by domain policy.

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