Java JVM中包含“@”的代理密码
我有一些Scala代码,它成功地协商(NTLM)代理并访问互联网,指定用户名和密码如下:Java JVM中包含“@”的代理密码,java,scala,proxy,jvm,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: Passwo
// 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
}
})
正在运行此程序的环境位于Linux机箱上的Spark群集(使用Spark submit
运行)。该代理为公司NTLM代理
如果我使用不包含@
的已知用户名和密码组合,则此操作有效。如果我将其更改为包含@
的,则它将失败
我曾尝试在urlEncode
函数中创建val res=str
(以防它不需要URL编码),尝试使用\\@
(使用和不使用URL编码)和^@
(使用和不使用URL编码)。每次我都会遇到一个异常,无法通过代理进行隧道。代理返回“需要HTTP/1.1 407代理授权”
我知道用户名和密码是有效的,因为它们当前在curl等成功使用的https\u proxy
变量中设置
因此,除非代理是在运行的Spark服务器中设置的这一事实以某种方式影响了它发生的情况,否则在我看来JVM库不支持代理的身份验证器中有
@
。问题不在java.net
库代码中(编辑:当然对于HTTP基本代理,我还没有能够测试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);
}
}
}
第一个答案:
您显示的代码是从JVM系统属性获取密码。您如何将密码放入该属性中?我怀疑问题出在那里,而不是您显示的代码中
如果您在Windows上,并且将密码设置为命令行参数,则需要使用DOS转义字符“^”,即
如果使用其他机制向Java提供密码,则可能需要不同的转义方案。这都是在Linux上,而不是在Windows上(因此我尝试使用反斜杠转义)。具体而言,这是我提交的Spark作业的一部分,并使用
--driver Java options='-Dhttps.proxyPassword指定此系统属性="p@ssword“..”
。我只是尝试了一个插入符号,以防万一,但没有骰子。感谢您的澄清。我很确定您的问题是如何在字符串通过各种参数层时转义此字符串,实际上与HTTP代理没有任何关系。您能记录“proxyPassword”的值吗“应用程序启动时的系统属性,以a)确认此假设和b)允许您更快地尝试不同的转义方案,并获得更清晰的反馈?我已经这样做了,并且它在进入密码验证之前打印出来时看起来是正确的。问题不在于您发布的代码中。我已经更新了我的答案。祝你好运好的,我已经设法修改了您的解决方案,使之与我的环境相匹配(公司代理、我试图运行它的同一个盒子等等),并且它仍然可以工作,因此这一定与通过Spark服务器有关。您可以捕获失败的Java客户端和代理之间以及成功的客户端和代理之间的网络流量,并将两者进行比较。这将很难分析,因为NTLM是一个挑战-响应协议,但在理论上它可能允许您缩小问题的范围。我已经开始这样做,但流量是一个艰难的过程。
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);
}
}
}
java -Dhttp.proxyPassword=foo^@bar -jar myapp.jar