我正在尝试打开到受 NTLM 身份验证方案保护的 url 的 http 连接。当我们在 Java 6 上时,这段代码已经正常工作了 2 年。我编写了一个小型 Java 程序,它访问那个特定的 url 以使测试用例尽可能简单。
问题是我无法使程序在 linux 上运行,并且在使用 JDK 7 版本时。Java 尝试 20 次访问 URL,然后我收到一个错误,告诉我服务器重定向太多次。它适用于 linux 和 JDK 6,以及适用于 JDK 6 或 7 的 windows 7。
我检查并尝试了此处列出的解决方案(以及许多其他解决方案):Getting "java.net.ProtocolException: Server redirected too many times" 错误。它没有用。我还必须补充一点,从浏览器访问 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 企业版 linux 6.4:Java(TM) SE 运行时环境(内部版本 1.6.0_33-b04)(64 位)
失败:
- Redhat 企业版 linux 6.4:Java(TM) SE 运行时环境(内部版本 1.7.0-b147)(64 位)
- Redhat 企业版 linux 6.4:Java(TM) SE 运行时环境(内部版本 1.7.0_05-b06)(64 位)
- Redhat 企业版 linux 6.4:Java(TM) SE 运行时环境(内部版本 1.7.0_13-b20)(64 位)
- Redhat 企业版 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”替换我试图强制执行的域,但由于用户存在于服务器上而不是服务器的域上,所以它失败了。
我不知道该怎么做。