407が繰り返されても、Javaが誤ったパスワードを送ったとは限りません。JDKのリファレンス実装は通常、HTTPS CONNECTトンネルのBasic認証を無効にしています。Basicしか提供しないプロキシは、認証コールバックが認証情報を渡す前に拒否される場合があります。

認証情報コールバックの範囲を限定する

JavaのHTTPクライアントにAuthenticatorを設定し、対象を設定済みのプロキシに限定します。

String proxyHost = "203.0.113.10";
int proxyPort = 8080;
String password = System.getenv("PROXY_PASSWORD");
if (password == null) {
    throw new IllegalStateException("Set PROXY_PASSWORD");
}

java.net.Authenticator auth = new java.net.Authenticator() {
    @Override
    protected java.net.PasswordAuthentication getPasswordAuthentication() {
        if (getRequestorType() == RequestorType.PROXY
                && proxyHost.equals(getRequestingHost())
                && proxyPort == getRequestingPort()) {
            return new java.net.PasswordAuthentication(
                "proxy-user", password.toCharArray());
        }
        return null;
    }
};

java.net.http.HttpClient client = java.net.http.HttpClient.newBuilder()
    .proxy(java.net.ProxySelector.of(
        new java.net.InetSocketAddress(proxyHost, proxyPort)))
    .authenticator(auth)
    .connectTimeout(java.time.Duration.ofSeconds(5))
    .build();

これはクライアントの設定コードであり、グローバルの既定認証器ではありません。文書用アドレスを置き換え、そのサービス用に実際に発行された認証情報を使います。要求元の種類と接続先を確認することで、同じコールバックがWebサイトの認証要求にプロキシアカウントを安易に渡すのを防ぎます。

どの認証段階で失敗しているか確認する

JDKには、通常のHTTPプロキシ利用とCONNECTトンネル向けに、それぞれ無効な認証方式の設定があります。設定名はjdk.http.auth.proxying.disabledSchemesとjdk.http.auth.tunneling.disabledSchemesです。

この違いにより、同じプロキシでもHTTP通信は認証でき、HTTPSトンネルでは失敗する場合があります。別のマシンでも実効的な既定値が同じだと決めつけず、実行環境のconf/net.propertiesとデプロイ方針を確認してください。

無効な方式のリストを全体で空にすることは、一般的なプロキシ修復方法ではありません。平文HTTPプロキシへ送るBasic認証情報は、その後に接続先と結ぶTLSでは保護されません。変更を認める前に、プロキシのトランスポート、認証方式、デプロイ方針を確認してください。

JDKの文書にある組み込み認証の動作も、プロキシが提示するすべての方式への対応を約束するものではありません。AuthorizationやProxy-Authorizationヘッダーを手動指定すると、自動認証器の処理の流れが変わります。コールバックが例と違う動作をする場合は、独自ヘッダーを調べてください。

関連記事