MQ

 View Only

Why is WebSphere Application Server (WAS) connecting to MQ using the user that started it rather than the user in the authentication alias?

By Paul Titheridge posted Fri November 29, 2024 09:18 AM

  

WebSphere Application Server (WAS) allows systems administrators to create authentication aliases. Each alias maps to a username and password. The alias can be set on Java message service (JMS) connection factories. This means that enterprise applications that create connections from those factories will pass the credentials to the JMS provider.

Here is an screenshot from the WebSphere Administrative Console that shows an authentication alias for the user "user1":

and here is a screenshot that shows a JMS connection factory which has been configured to use it:

Now, one question that comes up every so often is "Why is WAS flowing the user that started the application server to MQ when my application creates a JMS connection, rather than the user specified in the connection factory's authentication alias?". There are usually a couple of reasons for this.

Reason 1 : The application is doing a "direct lookup"

The first reason is that the application is looking up the connection factory using something called a "direct lookup", rather than an "indirect lookup". Let's look at what this means.

Here is some sample servlet code that looks up the JMS connection factory shown above:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 try { 
    // Connect to the application server's JNDI repository
    InitialContext ctx = new InitialContext();
    // Look up a JMS connection factory from JNDI
    String connectionFactoryName = "jms/connectionFactory";
    response.getWriter().append("doGet: Looking up connection factory: " + connectionFactoryName + "\n");
    ConnectionFactory connectionFactory = (ConnectionFactory) ctx.lookup(connectionFactoryName);
    // Create a connection to the queue manager using the connection factory
    response.getWriter().append("doGet: Creating connection\n");
    Connection connection = connectionFactory.createConnection();
    response.getWriter().append("doGet: Connection created\n");
    // Close the connection
    response.getWriter().append("doGet: Closing connection");
    connection.close();
  } catch (Exception e) {
    response.getWriter().append("doGet: Exception-->" + e + "n");
  }
  response.getWriter().append("doGet: Exiting\n");
}

Notice that the servlet code is looking up the connection factory using the JNDI name specified in the factory definition, like this:

This is known as a "direct lookup": You might think that this would causes the application to use the credentials in the connection factory's authentication alias when it creates the JMS connection. However, this is not the case. WAS doesn't use the authentication alias if an application does a "direct lookup", for security reasons. Instead, the user that started the application server is passed to MQ.

To get WAS to flow the username in the authentication alias, the application needs to use an "indirect lookup". Here, the application's deployment descriptor contains a resource reference which has its own JNDI name and the application looks up this name in JNDI. When the application is deployed, a WAS systems administrator will map that JNDI name to a connection factory defined in the application server.

Let's look at how we can do it with our sample servlet code.

The first thing we need to do is add a resource reference to the application. We can do this by putting the following XML into the application's web.xml file:

<resource-ref>
  <res-ref-name>myConnectionFactory</res-ref-name>
  <res-type>javax.jms.ConnectionFactory</res-type>
  <res-auth>Container</res-auth>
</resource-ref>

Now, we need to change the application code to look up the connection factory using this JNDI name, and not the JNDI name of the connection factory defined in WAS. To do this, this line in the servlet's doGet(HttpServletRequest, HttpServletResponse) method:

String connectionFactoryName = "jms/connectionFactory";

needs to be modified to look like this:

String connectionFactoryName = "java:comp/env/myConnectionFactory";

The java:comp/env string at the start here is important. This tells the application server that the resource being looked up is in defined in the part of the JNDI tree for the current component (in this case, the web container) rather than in the main part of the tree.

The final thing that needs to be done is to map the resource reference to the connection factory we defined earlier. We do this when deploying the application. Here is the panel in the WebSphere Administrative Console that shows where this is done:

Now, when the servlet runs, it performs an "indirect lookup" of the connection factory, via the resource reference. This means that the application server will allow it to use the authentication alias, and flow the username specified in the alias when creating a connection to MQ.

This is quite complicated, so here is a diagram that shows what this mapping looks like:

Hopefully that makes sense!

Reason 2 : The application has specified a res-auth of "Application"

Even if your application is doing an indirect lookup of a connection factory, it might still flow the user that started WAS to MQ when it connects to a queue manager. This can happen if the resource reference has the <res-auth> element set to "Application", as shown in the example below:

<resource-ref>
  <res-ref-name>myConnectionFactory</res-ref-name>
  <res-type>javax.jms.ConnectionFactory</res-type>
  <res-auth>Application</res-auth>
</resource-ref>

In this situation, the information in the authentication alias is ignored. Instead, the container expects the application to provide the credentials to use when connecting to MQ by passing them in on the ConnectionFactory.createConnection(String userName, String password) or ConnectionFactory.createContext(String userName, String password) call. If the application calls ConnectionFactory.createConnection() or ConnectionFactory.createContext(), then WAS will flow the user that started the application server.

So, for our servlet above, if we modify the resource reference so that <res-auth> is set to "Application", the Servlet code will also need to be changed, so that this line:

Connection connection = connectionFactory.createConnection();

is replaced with:

Connection connection = connectionFactory.createConnection("user1", "password");

where the username and password passed in are the ones stored in the authentication alias. One thing to note here is that the username and password are hard-coded into the application, which means that if either of them change the application needs to be recompiled, which is not ideal...

As always, I hope this helps! The Using authentication aliases with enterprise applications topic in the MQ documentation contains some additional information on this, including some handy tables that show the username that will be flowed to the queue manager for various different configurations, so its worth taking a look at that. If you have any questions, though, feel free to ask and I'll be happy to answer them.


#community-stories3
#community-stories3
0 comments
9 views

Permalink