As promised in my last post, in this post I will share with you how to play with the Weblogic API through JMX to handle tasks such as creating users, creating roles, assigning users to roles, and letting users change their own password. This post will clear out the gray areas in implementing user subscription or registration forms and the generation of salted-hashed passwords that can be recognized by weblogic. This post assumes that you have already properly set-up your SQLAuthenticator provider in weblogic.
Though this post is specific to SQLAuthenticator, the concept demonstrated herein is applicable to the Weblogic API as a whole. Just changing the MBEAN_INTERFACE constant to "weblogic.security.providers.authentication.DefaultAuthenticatorMBean", this post already applies to the DefaultAuthenticator.
Prerequisites
- Setup the SQLAuthenticator required schema
- Create a Datasource in weblogic console that points to the schema in step 1.
- Setup SQLAuthenticator provider in weblogic console.
Implementation
The key to playing with Weblogic APIs is through JMX (Java Management Extensions). I cant tell through experience that understanding JMX is not for the faint-hearted, but if you got it, you will realize how simple it is. Below is a sample adapter class that I device so that I will deal with JMX only once, and the rest of my application that needs to access the weblogic API will just point to my adapter class.package soadev.adapters; import java.io.Serializable; import java.io.IOException; import java.util.Hashtable; import javax.management.Descriptor; import javax.management.MBeanServerConnection; import javax.management.ObjectName; import javax.management.modelmbean.ModelMBeanInfo; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXServiceURL; import javax.naming.Context; public class SQLAuthenticatorAdapter implements Serializable { private static final String MBEAN_INTERFACE = "weblogic.security.providers.authentication.SQLAuthenticatorMBean"; private MBeanServerConnection connection; private JMXConnector connector; private ObjectName providerON; public void createUser(String username, String password, String description) throws Exception { connection.invoke(providerON, "createUser", new Object[] { username, password, description }, new String[] { "java.lang.String", "java.lang.String", "java.lang.String" }); } public void createGroup(String groupName, String description) throws Exception { connection.invoke(providerON, "createGroup", new Object[] { groupName, description }, new String[] { "java.lang.String", "java.lang.String" }); } public void addMemberToGroup(String groupName, String username)throws Exception{ connection.invoke(providerON, "addMemberToGroup", new Object[] { groupName, username }, new String[] { "java.lang.String", "java.lang.String" }); } public void changeUserPassword(String username, String oldPassword, String newPassword) throws Exception { connection.invoke(providerON, "changeUserPassword", new Object[] { username, oldPassword, newPassword }, new String[] { "java.lang.String", "java.lang.String", "java.lang.String" }); } public boolean isMember(String parentGroupName, String memberUserOrGroupName, boolean recursive)throws Exception{ return (Boolean) connection.invoke(providerON, "isMember", new Object[] { parentGroupName, memberUserOrGroupName, recursive }, new String []{"java.lang.String", "java.lang.String", "java.lang.Boolean"}); } private ObjectName getAuthenticationProviderObjectName(String type)throws Exception{ ObjectName defaultRealm = getDefaultRealm(); ObjectName[] atnProviders = (ObjectName[])connection.getAttribute(defaultRealm, "AuthenticationProviders"); ObjectName MBTservice = new ObjectName("com.bea:Name=MBeanTypeService,Type=weblogic.management.mbeanservers.MBeanTypeService"); for (int p = 0; atnProviders != null && p < atnProviders.length; p++) { ObjectName provider = atnProviders[p]; ModelMBeanInfo info = (ModelMBeanInfo)connection.getMBeanInfo(provider); Descriptor desc = info.getMBeanDescriptor(); String className = (String)desc.getFieldValue("interfaceClassName"); String[] mba = (String[])connection.invoke(MBTservice, "getSubtypes", new Object[] { type }, new String[] { "java.lang.String" }); for (int i = 0; i < mba.length; i++) { if (mba[i].equals(className)) { return provider; } } } return null; } private ObjectName getDefaultRealm() throws Exception { ObjectName service = new ObjectName("com.bea:Name=DomainRuntimeService,Type=weblogic.management.mbeanservers.domainruntime.DomainRuntimeServiceMBean"); ObjectName domainMBean = (ObjectName)connection.getAttribute(service, "DomainConfiguration"); ObjectName securityConfiguration = (ObjectName)connection.getAttribute(domainMBean, "SecurityConfiguration"); ObjectName defaultRealm = (ObjectName)connection.getAttribute(securityConfiguration, "DefaultRealm"); return defaultRealm; } public void connect(){ String hostname = "localhost"; String username = "weblogic"; String password = "weblogic1"; int port = 7101; connect(hostname, username, password, port); } public void connect(String hostname, String username, String password, int port){ try { String protocol = "t3"; String jndi = "/jndi/weblogic.management.mbeanservers.domainruntime"; JMXServiceURL serviceURL = new JMXServiceURL(protocol, hostname, port, jndi); Hashtable env = new Hashtable(); env.put(Context.SECURITY_PRINCIPAL, username); env.put(Context.SECURITY_CREDENTIALS, password); env.put(JMXConnectorFactory.PROTOCOL_PROVIDER_PACKAGES, "weblogic.management.remote"); env.put("jmx.remote.x.request.waiting.timeout", new Long(10000)); connector = JMXConnectorFactory.connect(serviceURL, env); connection = connector.getMBeanServerConnection(); providerON = getAuthenticationProviderObjectName(MBEAN_INTERFACE); } catch (Exception ex) { throw new RuntimeException(ex); } } public void close(){ try { connector.close(); } catch (IOException ioe) { throw new RuntimeException(ioe); } } }
Please update the connect() method with your own credentials and port information if necessary.
Below is a sample java client that utilize this adapter:
package soadev.client; import soadev.adapters.SQLAuthenticatorAdapter; public class SQLAuthenticatorAdapterClient { private SQLAuthenticatorAdapter adapter = new SQLAuthenticatorAdapter(); public static void main(String[] args) { SQLAuthenticatorAdapterClient client = new SQLAuthenticatorAdapterClient(); try { client.connect(); client.testCreateUser(); client.testCreateGroup(); client.testAddMemberTopGroup(); client.close(); } catch (Exception e) { // TODO: Add catch code client.close(); e.printStackTrace(); } } public void testCreateUser()throws Exception{ String username = "pino"; String password = "password1"; String displayName = "Pino SOADEV"; adapter.createUser(username, password, displayName); } public void testCreateGroup()throws Exception{ String groupName = "SOADevGroup"; String description = "This is a especial group created through SQLAuthenticatorAdapter"; adapter.createGroup(groupName, description); } public void testAddMemberTopGroup()throws Exception{ String username = "pino"; String groupName = "SOADevGroup"; adapter.addMemberToGroup(groupName, username); } public void close(){ adapter.close(); } public void connect(){ adapter.connect(); } }
Be sure that "Weblogic 10.3 Thin-Client" is included in the libraries and classpath of your client project.
After running the client above, you will see that user "pino", group "SOADevGroup, and the assignment of user "pino" to group "SOADevGroup" were persisted respectively in the USERS, GROUPS, and GROUPMEMBERS tables.Prototype
//TODOConclusion
This post demonstrates how easy it is to play with Weblogic API so that you have something as reference to jump-start your own implementation. More interfaces can be implemented by referring to the resources below. Be wary the examples above do not exemplify proper exception and concurrency handling.
Cheers!Pino