Friday, July 30, 2010

JMX with Spring and Annotation (Part 2)

Now that MBeanExporter is setup, we need to set the server-side connector to expose the MBeanServer.

For security concern, I decide to add in JMX authentication.  It is very complicated to setup with Java's configuration.  However, I managed to do so with Spring with two simple steps:
1. CustomJMXAuthenticator.java
package com.my.company.server;

import javax.management.remote.JMXAuthenticator;
import javax.management.remote.JMXPrincipal;
import javax.security.auth.Subject;

import org.apache.commons.lang.StringUtils;

/**
 * @author Yaohua Wang
 *
 */
public class CustomJMXAuthenticator implements JMXAuthenticator {

    private String userName;

    private String password;

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Subject authenticate(Object credentials) {
        if(credentials == null
            || !(credentials instanceof String[]))
            throw new SecurityException("Credentials are required!");

        String[] info = (String[]) credentials;
        if(StringUtils.equals(info[0], userName)
            && StringUtils.equals(info[1], password)){
            Subject subject = new Subject();
            subject.getPrincipals().add(new JMXPrincipal(userName));
            return subject;
        }
        throw new SecurityException("Unable to match credentials!");
    }
}

2. Spring configuration:
<bean id="registry"
    class="org.springframework.remoting.rmi.RmiRegistryFactoryBean"
    destroy-method="destroy" autowire="no">
    <property name="port" value="1099"/>
    <property name="alwaysCreate" value="true"/>
</bean>
...
<bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean" autowire="no" depends-on="registry">
    <property name="objectName" value="connector:name=rmi"/>
    <property name="serviceUrl"
        value="service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi"/>
    <property name="threaded" value="true"/>
    <property name="daemon" value="true"/>
    <property name="environmentMap">
        <map>
            <entry key="java.rmi.server.hostname" value="localhost"/>
            <entry key="jmx.remote.authenticator">
                <bean
class="com.my.company.server.CustomJMXAuthenticator">
                    <property name="userName" value="jmxuser"/>
                    <property name="password" value="jmxpassword"/>
                </bean>
            </entry>
        </map>
    </property>
</bean>

Now start the server and open jconsole.  Type in following information:
  • Remote Process: localhost:1099
  • User Name: jmxuser
  • Password: jmxpassword
You should see the domain "ChannelServer" in MBeans tab.

1 comment:

Anonymous said...

Great article!

Do you know how to register such an authenticator class using annotations (without the XML configuration, but only properties file) in Spring Boot? Preferable 1.5.3.RELEASE

Thanks in advance