您可以使用com.sun.management.jmxremote.login.config
management.properties 文件中的配置参数(请参阅 %JAVA_HOME%/lib/management/management.properties)来配置要使用的 Authenticator 和 LoginModule。
默认值如下:
JMXPluggableAuthenticator {
com.sun.jmx.remote.security.FileLoginModule required;
};
它读取纯文本密码文件jmxremote.password
。由于com.sun.jmx.remote.security.JMXPluggableAuthenticator
可以重新配置以使用任何 LoginModule 实现,您可以自由选择现有的 LoginModule 或实现自己的使用加密密码文件的 LoginModule。
要重新实现FileLoginModule
,您应该查看attemptAuthentication(boolean)
实际执行身份验证并且您可能要替换的方法。实现javax.security.auth.spi.LoginModule
接口并使用给定的 CallbackHandler(您将从 init() 方法中获取它)来请求用户名和密码。对收到的密码进行加密/散列处理,并将其与从加密密码文件中读取的密码进行比较。伪代码:
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 环境中运行,您可以轻松地使用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 将尝试使用 null 用户名和 null 密码进行身份验证,这被明确阻止。当用户没有输入任何用户名和密码时,VisualVM 通过使用空字符串作为用户名和密码来做得更好。
另请注意,远程连接时 NTLoginModule 不起作用,我认为您将不得不使用更复杂的登录模块,但 Sun 已经提供了足够的它们:
- com.sun.security.auth.module.Krb5LoginModule:使用 Kerberos 协议对用户进行身份验证
- com.sun.security.auth.module.LdapLoginModule:(Java 6 中的新功能):通过指定技术连接用户对 LDAP 服务器执行身份验证
- com.sun.security.auth.module.JndiLoginModule:针对在 JNDI 上下文中注册的 LDAP 服务器执行身份验证
- com.sun.security.auth.module.KeyStoreLoginModule:使用 Java 密钥库对用户进行身份验证。支持 PIN 或智能卡身份验证。
你会想看看LdapLoginModule