简体   繁体   English

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

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

I am trying to open an http connection to an url protected with the NTLM authentication scheme. 我正在尝试打开与NTLM身份验证方案保护的URL的http连接。 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. 当我们使用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. 问题是我无法使程序在Linux上运行,并且在使用JDK 7的版本时.Java尝试20次访问URL,然后我收到一个错误,告诉我服务器重定向次数太多。 It works fine with linux and JDK 6, and in windows 7 with JDK 6 or 7. 它适用于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 . 我检查并尝试了此处列出的解决方案(和许多其他人): 获取“java.net.ProtocolException:服务器重定向次数太多”错误 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. 我还要补充一点,当从浏览器访问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运行时环境(版本1.7.0_15-b03)(64位)
  • Windows 7: Java(TM) SE Runtime Environment (build 1.7.0_10-b18) (64 bit) Windows 7:Java(TM)SE运行时环境(版本1.7.0_10-b18)(64位)
  • Windows 7: Java(TM) SE Runtime Environment (build 1.6.0_33-b04) (64 bit) Windows 7:Java(TM)SE运行时环境(版本1.6.0_33-b04)(64位)
  • Redhat enterprise linux 6.4: Java(TM) SE Runtime Environment (build 1.6.0_33-b04) (64 bit) 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运行时环境(版本1.7.0-b147)(64位)
  • 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运行时环境(版本1.7.0_05-b06)(64位)
  • 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运行时环境(版本1.7.0_13-b20)(64位)
  • Redhat enterprise linux 6.4: Java(TM) SE Runtime Environment (build 1.7.0_15-b03) (64 bit) 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. 因此,由于服务器在域中被加入,它将作为NTLM协议的一部分发送回客户端。 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. Java每次都会替换我试图通过变量“domainFromServer”强制执行的域,并且由于用户存在于服务器而不是服务器的域上而失败。

I don't know exactly what to do with that. 我不知道该怎么做。

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. 我更改了Client.java类中的代码,并将其与com.sun.security.ntlm包的其余部分一起重新编译,然后创建了一个名为rt_fix.jar的jar,其中包含该特定包的类。 Then I used a java startup option to force it to load my jar before the internal rt.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中更改的代码,方法类型为3:

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. 它阻止Java在发送NTLM身份验证的第三部分时,使用从服务器收到的域更改我尝试进行身份验证的域。 The domain I was trying to authenticate to is in fact the name of the server because the user accounts are local. 我尝试进行身份验证的域实际上是服务器的名称,因为用户帐户是本地的。

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

Correct is this: 这是正确的:

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

这个问题在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

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. 我不小心阻止了我的域用户后,尝试使用不正确的密码登录时,我遇到了同样的错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM