15 Understanding WebLogic JMS Security
Learn how to secure WebLogic JMS resources using thread-based and object-based security models.
Securing WebLogic JMS Resources
WebLogic JMS enables you to secure JMS resources by restricting access to JMS destinations.
By default, all users can access JMS resources in a WebLogic Server or WebLogic cluster. This includes users with remote access, and users running directly in the WebLogic Server or WebLogic cluster itself. To restrict access to WebLogic JMS destinations, you must create security policies on the user's system resources and ensure users have the required roles. See Overview of Securing WebLogic Resources for more information about security roles and policies, and Java Messaging Service (JMS) Resources for policies available to JMS.
JMS Security Terminology
WebLogic JMS uses either object-based security (OBS) or thread-based security to determine which user is checked when accessing a secured WebLogic JMS destination.
Understand some common terminology used in the context of JMS security before exploring the difference between the two security approaches:
- Subject: The security object that represents a user in a WebLogic application.
- Principal and Credentials: A user's user name and password respectively.
- Credentials: Often used to represent the combination of a user's user name and password.
Thread-based security implies that the subject that a secured WebLogic JMS destination checks is implicitly derived from the current caller's thread. Object-based security implies that the subject is implicitly derived from a subject stored in the object the caller is using to make its JMS call. In general, WebLogic security is thread based. The following sections explore both approaches.
Understanding Thread-Based Security on Clients and Servers
By default, access to secured WebLogic JMS resources leverages thread-based security. This gives WebLogic JMS security behavior parity with Java EE's general security model for EJBs, web applications, and RMI.
- Checked using the security subject/role stored implicitly within the current thread.
- Not checked using the user name and password that can be passed to JMS javax.jms
createConnection()
orcreateJMSContext()
calls. In other words, the thread's subject from (1) supersedes the user name and password that an application can optionally pass intocreateConnection()
orcreateJMSContext()
.
Thread-Based Security for Server Applications
For server-side applications, there are multiple ways to set EJB and Web application thread's subject or role. See Oracle Fusion Middleware Developing Applications with the WebLogic Security Service. To override thread-based JMS security checking behavior for server-side WebLogic JMS send and consume calls, see Understanding Object Based Security on Server Applications.
Thread-Based Security for Client Applications
For client applications, the current thread's subject
is generated and implicitly placed on the thread when the client application creates
a JNDI context. A JNDI context can optionally specify credentials by using its
SECURITY_PRINCIPAL
and
Context.SECURITY_CREDENTIALS
properties.
java.util.Hashtable env = new Hashtable();
env.put(Context.PROVIDER_URL, url); // typical url: t3://example.com:7001
env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
env.put(Context.SECURITY_PRINCIPAL, "myusername");
env.put(Context.SECURITY_CREDENTIALS, "user_password");
javax.naming.InitialContext ic = new InitialContext(env); // throws an exception if user name/password is incorrect
// thread now implicitly has subject for the ic user name/password
ic.close();
// thread now has original subject from before the ic was created
If a client creates an InitialContext
object without
specifying credentials, then:
- The subject already on the current thread is unchanged.
- If there is no subject on the thread, then it is assumed to be an anonymous subject.
If a client program needs to transfer a thread's subject to a
different thread than the thread used to create an InitialContext
object, the client program can use security APIs to store the current subject of the
thread and then subsequently use this cached subject in a different thread.
// retrieve the subject that is implicitly store in the current thread
javax.security.auth.Subject subject =
weblogic.security.Security.getCurrentSubject();
...
// use the given subject to perform an action:
// if the action throws, return an exception
// if the action succeeds, return "OK"
static Object doSomethingAsSubject(javax.security.auth.Subject subject) {
try {
return weblogic.security.Security.runAs(subject,
new java.security.PrivilegedExceptionAction() {
public java.lang.Object run() throws Exception {
// do something or throw
return "OK";
}});
} catch (java.security.PrivilegedActionException e) {
return e;
} catch (Throwable t) {
return t;
}
}
This example code pattern can also be used to switch the subject on a thread for the use case where one single JMS client communicates with two domains at the same time, see Programming Pattern for a Single JMS Client Communicating With Two WebLogic Domains.
javax.security.auth.Subject anon = new javax.security.auth.Subject();
To override thread-based JMS security checking behavior for client-side WebLogic JMS send and consume calls, see Understanding Object-Based Security on Clients.
Understanding Object-Based Security
WebLogic JMS clients can optionally use a simpler security model called object-based security (OBS) instead of thread-based security. This option was introduced in WebLogic 12.2.1.3 and is useful for multithreaded clients which otherwise need extra code to transfer thread-based security subjects between threads.
The following sections explain how to enable object-based security:
Enabling Object-Based Security on Clients
Enabling object-based security (OBS) causes message send and consume security checks to be based on credentials specified during JMS client initialization instead of on the calling thread's subject.
Enabling OBS requires using an OBS JNDI initial context. Any WebLogic JMS senders or consumers that are created using an OBS connection factory that is obtained from an OBS initial context will, by default, implicitly use the credential that is associated with the OBS initial context instead of the subject that is associated with the current sender or consumer thread. In addition, if a user name and password credential is passed as parameters to a standard JMS createConnection()
or createJMSContext()
call on an OBS connection factory, then this new credential supersedes the credential that is associated with the OBS initial context and the new credential will be used for sends or consumes on that connection or JMS context.
Steps to enable OBS on a JMS client's senders and consumers:
Object-Based Security Limitations on Clients
Listed below are some of the limitations of OBS:
- An OBS initial context only supports
lookup()
calls and will otherwise throwNotSupported
exceptions. If you need a context that supports other calls, then create a second context that does not enable OBS. - An OBS initial context is supported only on WebLogic clients and is not supported on WebLogic Servers. An exception is thrown when attempting to use such a context in combination with WebLogic JMS server facilities like bridges, MDBs, or resource references. This restriction exists because these server-side JMS facilities already have their own security handling that provides similar semantics to OBS.
- When an OBS initial context is created, the initial context's user is not placed on the current thread. This differs from a
weblogic.jndi.WLInitialContextFactory
context.
Enabling Object-Based Security on Server Applications
Learn how to enable object-based security (OBS) for inbound and outbound JMS applications:
Object-Based Security for Inbound JMS Applications
- Supplied with the application itself.
Note:
Oracle does not recommend using this method. - Defined on the service itself (Messaging Bridges allow you to configure user name or password).
Note:
Oracle does not recommend using this method. - Just as for the outbound case, specified using a foreign JMS Server in a JMS system resource module that maps a JMS resource into JNDI.
- Ensures the credentials are dynamically configurable and not hard coded into an application or descriptor file.
- Applies to almost all inbound and outbound use cases so it is useful as a way to centrally manage your JMS credentials.
Object-Based Security for Outbound JMS Applications
Server applications that make outbound WebLogic JMS calls can achieve an object-based security pattern that supersedes the current security subject on the current thread. For this to work, ensure the following:
- Map the current JNDI location of a JMS connection factory to a local JNDI by:
- Configuring a foreign JMS Server in a JMS system resource module.
- Configuring credentials in the foreign JMS Server for users who have the required permissions.
- In the application code, use a JMS resource reference or inject a JMS context that references the local JNDI name of the JMS connection factory.
Note:
It is a general best practice for server applications to use resource references or JMS context injection to reference a JMS connection factory regardless of whether you need an object-based security pattern or a thread-based security pattern.
After the above requirements are met, WebLogic Server subsequently injects the credentials that you configured in the foreign JMS Server into every outbound send or consume call that originates from the given JMS connection factory.
Understanding Cross-Domain Security
By default, WebLogic Server security is thread-based, which means operations are performed as the "user" that is associated with the current thread. See Understanding Thread-Based Security on Clients and Servers. When an application uses JMS to communicate with multiple domains, the application needs to ensure that the correct user is used when it communicates with each of the domains that are involved.
In addition, in order to secure the internal communication between WebLogic Server instances across domain boundaries, cross-domain security needs to be established between the domains. Using a cross-domain security configuration, WebLogic Server establishes a security role for cross-domain users, and uses the WebLogic Credential Mapping security provider in each domain to store the credentials to be used by the cross-domain users. You can enable cross-domain security in a per domain basis. A cross-domain credential mapping must be configured for each remote domain where internal communications need to be secure. Details about cross-domain security configuration are discussed in Configuring Cross-Domain Security in Administering Security for Oracle WebLogic Server. Guidelines are provided for using the cross-domain security in various cross-domain scenarios. See Cross-Domain Security Guidelines.
Note:
The examples provided are example code for illustrating security patterns, not working code that conforms to all JMS coding best practices. See Configuration Best Practices.
Cross-Domain Security Guidelines
Follow these guidelines while configuring cross-domain security:
-
If your Message-Driven Bean (MDB) deployment and the JMS destination that the MDB listens on, are in different WebLogic domains, you need to consider configuring cross-domain security between the two domains in combination with a foreign JMS server. For more details, see Using MDBs With Cross Domain Security in Developing Message-Driven Beans for Oracle WebLogic Server.
-
If your application or a messaging bridge participates in global transactions that involve more than one WebLogic domain, you need to consider configuring cross-domain security between the two domains. This applies to messaging bridges between two WebLogic domains with an exactly-once QoS. For more details, see Configuring Cross Domain Security in Developing JTA Applications for Oracle WebLogic Server
-
If your application in one WebLogic domain accesses WebLogic Server JMS distributed destinations in another WebLogic domain, you need to consider configuring cross-domain security between the two domains. In addition, the application should use a foreign JMS server in combination with a standard Java EE resource reference to reference the remote destination.
-
If your application uses WebLogic Server JMS store-and-forward to forward messages from one WebLogic domain to another WebLogic domain, you need to consider configuring cross-domain security between the two domains. See SAF and Cross Domain Security in Administering the Store-and-Forward Service for Oracle WebLogic Server.
-
If a single JMS client communicates with multiple domains at the same time, the application code may need to manage switching users back and forth corresponding to the domain that the application talks to using the same thread. See the
runAs helper
method described in Thread-Based Security for Client Applications. -
If using a foreign JMS server in a cross-domain scenario without combining this feature with a Java EE resource reference, MDB, or messaging bridge, a built-in helper API can be used to correctly handle security credential propagation.
Note:
It is a best practice to use a Java EE resource reference, MDB, or messaging bridge instead of a helper API.
Programming Pattern for a Single JMS Client Communicating With Two WebLogic Domains
A single JMS client that is not running on a WebLogic Server may need to communicate with two (or more) WebLogic domains at the same time while specifying a valid thread-based security subject for each.
Here is an example of an application that incorrectly handles security subjects while communicating with two domains. It attempts to receive request messages from a JMS destination in one domain and send response messages to a destination in another domain as indicated in the following code excerpt.
// This sample code INCORRECTLY handles security subjects
// in a client that communicates between two domains. It is
// intended to forward JMS messages.
java.util.Hashtable env = new Hashtable();
env.put(Context.PROVIDER_URL, domain1_url); // typical url: t3://example.com:7001
env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
env.put(Context.SECURITY_PRINCIPAL, "mydomain1_username");
env.put(Context.SECURITY_CREDENTIALS, "mydomain1_password");
javax.naming.InitialContext ctx = new InitialContext(env);
// thread now implicitly has subject that is valid in domain1
final Destination reqDest = (Destination) ctx.lookup(reqDestJNDI);
final ConnectionFactory cf = (ConnectionFactory) ctx.lookup(reqCfJNDI);
final MessageConsumer consumer = cf.createContext().createConsumer(reqDest);
java.util.Hashtable env2 = new Hashtable();
env2.put(Context.PROVIDER_URL, domain2_url); // typical url: t3://example.com:7001
env2.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
env2.put(Context.SECURITY_PRINCIPAL, "mydomain2_username");
env2.put(Context.SECURITY_CREDENTIALS, "mydomain2_password");
javax.naming.InitialContext ctx2 = new InitialContext(env2);
// thread now implicitly has subject that is valid in domain2
final Destination resDest = (Destination) ctx2.lookup(resDestJNDI);
final ConnectionFactory cf2 = (ConnectionFactory) ctx.lookup(resCfJNDI);
// create JMS producer to send response msg to domain2 now
MessageProducer producer = cf2.createConext().createProducer(resDest);
do {
// !!The following operation may fail since the current thread has the subject that is only valid in domain2
// while the consumer tries to talk to domain1.
Message msg = consumer.receive(1000);
if (msg != null) {
// process msg and generate response message resMsg
producer.send(resMsg);
}
} while (msg != null);
In the above example, the receive
operation may fail since the current
thread has the subject that is only valid in domain2
while the consumer
tries to talk in domain1
.
To resolve this issue, the JMS client application code needs to use the programming pattern discussed in Thread-Based Security for Client Applications to cache the subjects each time after a new initial context is created, and to restore it on the thread as needed. The following code illustrates the necessary changes to the previous example code.
java.util.Hashtable env = new Hashtable();
env.put(Context.PROVIDER_URL, domain1_url); // typical url: t3://example.com:7001
env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
env.put(Context.SECURITY_PRINCIPAL, "mydomain1_username");
env.put(Context.SECURITY_CREDENTIALS, "mydomain1_password");
javax.naming.InitialContext ctx = new InitialContext(env);
// thread now implicitly has subject that is valid in domain1
// retrieve the subject that is implicitly stored in the current thread
javax.security.auth.Subject domain1Subject = weblogic.security.Security.getCurrentSubject();
final Destination reqDest = (Destination) ctx.lookup(reqDestJNDI);
final ConnectionFactory cf = (ConnectionFactory) ctx.lookup(reqCfJNDI);
// create JMS consumer to receive from domain1 now
MessageConsumer consumer = cf.createContext().createConsumer(reqDest);
java.util.Hashtable env2 = new Hashtable();
env2.put(Context.PROVIDER_URL, domain2_url); // typical url: t3://example.com:7001
env2.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
env2.put(Context.SECURITY_PRINCIPAL, "mydomain2_username");
env2.put(Context.SECURITY_CREDENTIALS, "mydomain2_password");
javax.naming.InitialContext ctx2 = new InitialContext(env2);
// thread now implicitly has subject that is valid in domain2
final Destination resDest = (Destination) ctx2.lookup(resDestJNDI);
final ConnectionFactory cf2 = (ConnectionFactory) ctx.lookup(resCfJNDI);
// we can create JMS producer to send response msg to domain2 now
MessageProducer producer = cf2.createConext().createProducer(resDest);
do{
// use the given subject to perform an action:
try {
Message msg = (Message) weblogic.security.Security.runAs(domain1Subject,
new.java.security.PrivilegedExceptionAction() {
public java.lang.Object run()throws Exception {
return consumer.receive(1000);
}});
} catch (java.security.PrivilegedActionException e) {
// handle securty exception
} catch (Throwable t) {
// handle other throwables
}
// the current thread still has the credentials for domain2
if (msg ) {
// process msg and generate response message resMsg
producer.send(resMsg);
}
} while (msg != null);
Programming Patterns for Using a Foreign JMS Server Between Two WebLogic Domains
Note:
This section does not apply if you are using a foreign JMS server in combination with Java EE resource references, messaging bridges, or MDBs. It is a best practice to use these features when possible because then no special case security handling code is needed as long as cross-domain security is properly configured between the domains.You can use a foreign JMS server to map JMS resources from one WebLogic domain to another WebLogic domain. When a client or a Java EE application directly looks up the local JNDI name of a foreign JMS destination mapping, a lookup-by-reference is implicitly performed. The remote credentials that are configured in a foreign JMS server are automatically (temporarily) placed on the thread during this lookup by reference, and the thread will resume the local subject that was on the thread before the lookup, after the lookup returns to the client. But note that the remote credentials from a foreign JMS server are not implicitly used for subsequent JMS operations unless you are also using MDBs, messaging bridges, or resource references. As a result, any subsequent JMS calls to a remote domain that do not use these features will cause an access denied error if the JMS resources in the remote domain are protected, even when a foreign JMS configuration contains the correct credentials.
The following code example shows how to use the
JMSDestinationAvailabilityHelper
API to make sure that the
correct remote credentials are on the thread when accessing a remote JMS resources
using a foreign JMS server without also using a Java EE resource reference, MDB, or
messaging bridge. For more information about using the
JMSDestinationAvailabilityHelper
, see Advanced Programming with Distributed Destinations Using the JMS Destination Availability Helper API
Hashtable h = new Hashtable();
h.put(Context.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");
h.put(Context.PROVIDER_URL, url);
h.put(Context.SECURITY_PRINCIPAL, user);
h.put(Context.SECURITY_CREDENTIALS, password);
RegistrationHandle handle = JMSDestinationAvailabilityHelper.getInstance().register(h, destJNDI, new MyDAHelperListener());
final Context ctx = new InitialContext(h);
final Destination queue = (Destination) ctx.lookup(destJNDI);
final ConnectionFactory cf= (ConnectionFactory) ctx.lookup(cfJNDI);
handle.runAs(
new PrivilegedExceptionAction(){
public Object run()throws Exception{
JMSContext context = cf.createContext();
for(int i=0; i<msgcount ; i++){
String msg= text + i;
context.createProducer().send(queue,msg);
}
context.close();
return null;
}
}
);
ctx.close();
handle.unregister();
}
private class MyDAHelperListener implements DestinationAvailabilityListener {
public void onDestinationsAvailable(String destJNDIName, List<DestinationDetail> list) {
// no op for this particular example
}
public void onDestinationsUnavailable(String destJNDIName, List<DestinationDetail> list){
// no op for this particular example
}
public void onFailure(String destJNDIName, Exception exception) {
// no op for this particular example
}
}