11

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は失敗します。

どうしたらいいのかよくわかりません。

4

5 に答える 5

4

Client.java クラスのコードを変更し、残りの com.sun.security.ntlm パッケージと共に再コンパイルしてから、その特定のパッケージのクラスを含む rt_fix.jar という jar を作成しました。次に、Java 起動オプションを使用して、内部 rt.jar の前に jar を強制的にロードしました。

-Xbootclasspath/p:/path_to_jar/rt_fix.jar

私はこの解決策が好きではありませんが、うまくいきました。

メソッド type3 の Client.java で変更したコードを次に示します。

前 :

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

後 :

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

NTLM認証の3番目の部分を送信するときに、サーバーから受信したドメインで認証しようとしているドメインをJavaが変更するのを防ぎます。ユーザー アカウントがローカルであるため、認証しようとしていたドメインは実際にはサーバーの名前です。

于 2013-03-01T20:04:09.843 に答える