我正在使用JConsole访问我的应用程序MBean,并且使用了password.properties文件。但是根据Sun的规范,此文件仅包含明文格式的密码。
com.sun.management.jmxremote.password.file=<someLocation>/password.properties
现在,我想对密码进行加密并将其用于JConsole的JMX用户身份验证(“远程”部分中的“用户名”和“密码”字段)。我可以使用任何预定义的加密逻辑或自己的加密算法。
是否有人知道将这种纯文本密码更改为加密密码,这样JMX Framework也知道加密密码吗?
我当前的密码文件:
guest guest admin admin
使用加密,它应该看起来像:
guest ENC(RjqpRYbAOwbAfAEDBdHJ7Q4l/GO5IoJidZctNT5oG64=) admin ENC(psg3EnDei6fVRuqHeLwOqNTgIWkwQTjI2+u2O7MXXWc=)
您可以使用com.sun.management.jmxremote.login.configmanagement.properties文件中的配置参数(请参阅%JAVA_HOME%/ lib / management / management.properties)来配置要使用的Authenticator和LoginModule。
com.sun.management.jmxremote.login.config
默认值如下:
JMXPluggableAuthenticator { com.sun.jmx.remote.security.FileLoginModule required; };
读取纯文本密码文件jmxremote.password。由于com.sun.jmx.remote.security.JMXPluggableAuthenticator可以将其重新配置为使用任何LoginModule实现,因此您可以自由选择现有的LoginModule,也可以自由地使用加密的密码文件来实现自己的LoginModule。
jmxremote.password
com.sun.jmx.remote.security.JMXPluggableAuthenticator
要重新实现FileLoginModule,您应该看一下该attemptAuthentication(boolean)方法,该方法实际上执行身份验证,并且您可能将要替换它。实现该javax.security.auth.spi.LoginModule接口并使用给定的CallbackHandler(您将从init()方法中获取该接口)询问用户名和密码。加密/散列收到的密码,并将其与从加密密码文件读取的密码进行比较。伪代码:
FileLoginModule
attemptAuthentication(boolean)
javax.security.auth.spi.LoginModule
public class EncryptedFileLoginModule implements LoginModule { @Override public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) { this.subject = subject; this.callbackHandler = callbackHandler; } public boolean login() throws LoginException { attemptLogin(); if (username == null || password == null) { throw new LoginException("Either no username or no password specified"); } MessageDigest instance = MessageDigest.getInstance("SHA-1"); byte[] raw = new String(password).getBytes(); byte[] crypted = instance.digest(raw); // TODO: Compare to the one stored locally if (!authenticated) throw new LoginException(); return true; } private void attemptLogin() throws LoginException { Callback[] callbacks = new Callback[2]; callbacks[0] = new NameCallback("username"); callbacks[1] = new PasswordCallback("password", false); callbackHandler.handle(callbacks); username = ((NameCallback) callbacks[0]).getName(); user = new JMXPrincipal(username); char[] tmpPassword = ((PasswordCallback) callbacks[1]).getPassword(); password = new char[tmpPassword.length]; System.arraycopy(tmpPassword, 0, password, 0, tmpPassword.length); ((PasswordCallback) callbacks[1]).clearPassword(); }
但是,由于这已经是服务器端,因此如果您不通过SSL强制实施JMX,密码仍然会以纯文本格式传输。因此,可以强制实施SSL或使用另一种传输协议机制来对凭据进行编码,然后再通过有线传输它们。
总而言之,最好依赖JAAS提供的现有身份验证机制。例如,如果您在本地Windows环境中运行,则可以轻松地使用NTLoginModulefor自动登录。但这仅适用于本地计算机。
NTLoginModule
创建一个文件c:/temp/mysecurity.cfg:
MyLoginModule { com.sun.security.auth.module.NTLoginModule REQUIRED debug=true debugNative=true; };
接下来,配置jmxremote.access文件以包含您希望授予对JMX服务器访问权限的用户名或角色:
monitorRole readonly controlRole readwrite ... mhaller readonly
(我建议启用调试模式,直到它起作用为止。当用户尝试登录时,您将看到所有用户名,域名和组名。)为服务器设置以下JVM参数:
-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8686 -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.ssl=true -Djava.net.preferIPv4Stack=true -Djava.security.auth.login.config=c:/temp/mysecurity.cfg -Dcom.sun.management.jmxremote.login.config=MyLoginModule
启动您的应用程序,然后尝试使用JConsole或VisualVM进行连接。
请注意,尽管将不使用JConsole,但您需要指定用户名和密码。任何密码和用户名都可以使用。原因是因为jconsole将尝试使用空的用户名和空密码进行身份验证,这将被明确阻止。当用户未输入用户名和密码时,VisualVM通过使用空字符串作为用户名和密码来做得更好。
还要注意,远程连接时NTLoginModule不起作用,我认为您必须使用更复杂的登录模块,但Sun已经提供了足够的登录模块:
您将要看看 LdapLoginModule