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

时间:2017-08-18 05:26:46

标签: java scala proxy jvm

我有一些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
  }
})

但是,我现在已经获得了一个新的系统用户名/密码组合,密码中包含@。我已尝试直接使用密码,将其转义(如果需要进行双级转义,则使用\\\),对其进行网址编码(即替换{{1}使用@)甚至HTML编码(%40&commat;)都无济于事。

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

有什么想法吗?

修改

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

http_proxy

运行此环境的环境位于Linux机器上的Spark群集(使用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 } }) 运行)中。代理是公司的NTLM代理。

如果我使用已知的用户名和密码组合且不包含spark-submit,则可以使用。如果我将其更改为包含@的那个,则会失败。

我已经尝试在@功能中制作val res = str(如果它不需要进行网址编码),请尝试urlEncode(有和没有) URL编码)和\\@(带和不带URL编码)。我每次都得到^@的例外。

我知道用户名和密码是有效的,因为它们当前在curl等成功使用的Unable to tunnel through proxy. Proxy returns "HTTP/1.1 407 Proxy Authorization Required"变量中设置。

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

1 个答案:

答案 0 :(得分:6)

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

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

我认为您的问题必须与您在目前为止在问题中包含的代码之外配置系统的方式有关。

例如,您是否已将代理主机名传递给JVM或附近的系统,使其被&#34; @&#34;符号

请您提供更多背景信息?

演示代码以验证java.net库代码可以处理&#34; @&#34;在密码

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

代码按原样成功运行,如果我更改密码&#34;则代码失败。变量为不正确的密码。

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);
        }
    }
}

第一个回答:

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

如果您使用的是Windows,并且如果要将密码设置为命令行参数,则需要使用DOS转义字符&#34; ^&#34;,即

java -Dhttp.proxyPassword=foo^@bar -jar myapp.jar

如果您使用其他机制为Java提供密码,则可能需要一个不同的转义方案。