NTLM認証方式で保護されたURLへのhttp接続を開こうとしています。このコードは、Java 6を使用していた2年間、正しく機能しています。テストケースをできるだけ単純にするために、その特定のURLにアクセスする小さなJavaプログラムを作成しました。
問題は、Linuxでプログラムを動作させることができず、JDK 7のバージョンを使用している場合です。JavaがURLへのアクセスを20回試行すると、サーバーが何度もリダイレクトされたというエラーが表示されます。LinuxとJDK6で正常に動作し、Windows7ではJDK6または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」に置き換えますが、ユーザーがサーバーのドメインではなくサーバーに存在するため、Javaは失敗します。
どうしたらいいのかよくわかりません。