在JVM中包含`@`的代理密码

iva*_*anm 13 java proxy jvm scala

我有一些Scala代码已成功协商(NTLM)代理并访问互联网,指定用户名和密码如下:

// Based upon http://rolandtapken.de/blog/2012-04/java-process-httpproxyuser-and-httpproxypassword
Authenticator.setDefault(new Authenticator() {

  override protected def getPasswordAuthentication: PasswordAuthentication = {

    if (getRequestorType eq RequestorType.PROXY) {
      val prot = getRequestingProtocol.toLowerCase

      // This allows us to only return a PasswordAuthentication when we have
      // all four of the host, port, user and password _and_ the host and
      // port match the actual proxy wanting authentication.
      for {
        host <- Option(System.getProperty(prot + ".proxyHost"))
        if getRequestingHost.equalsIgnoreCase(host)
        port <- Try(augmentString(System.getProperty(prot + ".proxyPort")).toInt).toOption
        if port == getRequestingPort
        user <- Option(System.getProperty(prot + ".proxyUser"))
        pass <- Option(System.getProperty(prot + ".proxyPassword"))
      } yield return new PasswordAuthentication(user, pass.toCharArray)
    }

    // One of the if-statements failed.  No authentication for you!
    null
  }
})
Run Code Online (Sandbox Code Playgroud)

但是,我现在已经获得了一个新的系统用户名/密码组合,密码中包含一个密码@.我直接使用密码,逃避它(既尝试\\\的情况下,转义需要的双级),URL编码它(即替换@%40),甚至HTML编码(&commat;&#64;)无济于事.

我知道密码有效,因为它在其他系统上用于非JVM应用程序通过设置http_proxy变量来访问互联网,但它在这里不起作用.

有任何想法吗?


编辑

为了尝试清除一些内容,我尝试将代码简化为:

Authenticator.setDefault(new Authenticator() {

  def urlEncode(str: String): String = {
    val res = URLEncoder.encode(str, "UTF-8")
    // To confirm it's working
    println(s"${str} -> ${res}")
    res
  }

  override protected def getPasswordAuthentication: PasswordAuthentication = {

    if (getRequestorType eq RequestorType.PROXY) {

      return new PasswordAuthentication(urlEncode("username"), urlEncode("p@ssword").toCharArray);
    }
    null
  }

})
Run Code Online (Sandbox Code Playgroud)

运行此环境的环境spark-submit位于Linux机器上的Spark群集(运行)中.代理是公司的NTLM代理.

如果我使用已知的用户名和密码组合不包含,@那么这是有效的.如果我将其更改为包含a @然后失败.

我已经尝试val res = strurlEncode函数内部(如果它不需要URL编码),尝试\\@(有和没有URL编码)和^@(有和没有URL编码).每次我都得到例外Unable to tunnel through proxy. Proxy returns "HTTP/1.1 407 Proxy Authorization Required".

我知道用户名和密码是有效的,因为它们当前在https_proxycurl成功使用的变量中设置,等等.

因此,除非在正在运行的Spark服务器中设置代理的事实以某种方式影响它发生的事情,否则在我看来JVM库不支持@在代理的验证器中(至少).

Ric*_*ich 6

问题不在java.net库代码中(编辑:当然对于HTTP Basic代理,我还没有能够测试NTLM代理).该java.net代码可以使用密码与他们"@"正常连接.我已经在下面放了演示代码,允许您验证此声明.

您不需要转义传入的字符串值java.net.PasswordAuthentication,您应该在那里以明文传递密码.java.net当通过网络将代码发送到代理时,库代码将根据需要对密码进行编码(请参阅下面的演示代码以验证此声明).

我相信您的问题必须与您在目前为止在问题中包含的代码之外配置系统的方式相同.

例如,您是否已将代理主机名传递给JVM或附近的系统,使其被"@"符号混淆?

请你提供更多背景信息?

演示代码以验证java.net库代码可以在密码中处理"@"

此代码包含有关在本地计算机上将Fiddler2设置为HTTP代理,将Fiddler2配置为需要密码以及使用java.net库类连接该代理的说明.

代码按原样成功运行,如果我将"password"变量更改为错误密码,则代码失败.

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.net.URL;
import java.util.Base64;

public class q45749081 {

    public static void main(String[] args) throws Exception {

        // Start Fiddler HTTP proxy https://www.telerik.com/download/fiddler
        // Click "rules" -> "require proxy authentication"
        // Type into the "QuickExec" box below Fiddler's Web Sessions list:
        //    prefs set fiddler.proxy.creds dXNlcm5hbWU6cEBzc3dvcmQ=
        //
        // This sets Fiddler to require auth with "username", "p@ssword"
        //
        // See https://stackoverflow.com/questions/18259969/changing-username-and-password-of-fiddler-proxy-server

        // Note that you must start a new process each time you change the password
        // here, as sun.net.www.protocol.http.HttpURLConnection caches the proxy password
        // for the lifetime of the JVM process
        String password = "p@ssword";

        System.out.println(
            "prefs set fiddler.proxy.creds " +
            Base64.getEncoder().encodeToString("username:p@ssword".getBytes()));

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


        System.setProperty("http.proxyHost", "localhost");
        System.setProperty("http.proxyPort", "8888");

        System.out.println("Connecting to Google via authenticated proxy with password '"
            + password + "'");
        try (InputStream conn = new URL("http://www.google.com/").openStream()) {
            try (BufferedReader r = new BufferedReader(new InputStreamReader(conn))) {
                System.out.println(r.readLine());
                System.out.println("OK");
            }
        } catch (Exception e) {
            System.out.println("Failed: " + e);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

第一个答案:

您显示的代码是从JVM系统属性获取密码.你是如何将密码放入该财产的?我怀疑问题出在那里,而不是你所展示的代码中.

如果您在Windows上,并且如果您将密码设置为命令行参数,则需要使用DOS转义字符"^",即

java -Dhttp.proxyPassword=foo^@bar -jar myapp.jar
Run Code Online (Sandbox Code Playgroud)

如果使用其他机制为Java提供密码,则可能需要使用其他转义方案.