Imagine that you have some external system say, a Java client or perhaps even an external web server that authenticates a user, and you now want this user to participate in actions involving WebLogic. Furthermore, you don't want WebLogic to reauthenticate the user. Rather, you want to use some token generated by the external system to be used as an automatic WebLogic login. This is fairly typical of many single sign-on scenarios. The key to implementing this is to use an Identity Assertion Provider. Let's look at how you can implement such a scenario.
We are going to take as an example an external Java client that has presumably performed some user authentication, and who now needs to transfer this identity to WebLogic in order to access a protected web application. First of all, let's configure the web application to use identity assertion. Do this by setting the login-config to use a CLIENT-CERT authorization method. As this is standard J2EE, you will need to create a web.xml file with something such as the following in it:
nyse mysecrole CLIENT-CERT myrealm mysecrole
Now let's imagine we have a client (written in whatever language you wish) that has already performed some user authentication and now needs to access one of the protected web pages say, http://10.0.10.10:8001/index.jsp. The following client is such an example:
URL url = new URL("http://10.0.10.10:8001/index.jsp"); URLConnection connection = url.openConnection( ); BufferedReader in = new BufferedReader(new InputStreamReader( connection.getInputStream( ))); // Read the input stream in.close( );
If you simply run this program, you can expect an IOException when you try and access the input stream. This will be the 401 HTTP error code indicating that you are not authorized. We are going to get around this by making the client supply a token, and then configuring an Identity Assertion Provider to accept this token and authorize the user. Identity Assertion Providers can automatically take advantage of request cookies or headers. If WebLogic finds, for example, a header property with the same name as a token (we will see in a moment how to configure the identity provider with token names), it assumes that the content of the header property is the value of the token. The token we will use is a simple string that we are going to send in the HTTP request header when we create the connection to the server. To this end, modify the preceding code to read as follows:
URL url = new URL(urlAddr); URLConnection connection = url.openConnection( ); connection.setRequestProperty("MyToken",encodedToken); // Everything as before
The name of the request property, MyToken in our example, is significant. This is interpreted as the type of the token, which we will see later. A small caveat here is that WebLogic always expects incoming tokens to be Base 64-encoded. You can do this by using the utility class weblogic.utils.encoders.BASE64Encoder. So, to create an encoded token, you can write something such as this:
String token = "jon"; BASE64Encoder encoder = new BASE64Encoder( ); String encodedToken = encoder.encodeBuffer(token.getBytes( ));
The text that you place in the token can be anything you please, as long as your Identity Assertion Provider can read it. In our example, we will use a simple string, which we take to represent the authenticated user.
|
All that's left now is to create an Identity Assertion Provider. The MBean definition file used in our example is given in Example 17-8 in full.
Example 17-8. MyA.xml, the MDF file for the assertion provider
Note the following things:
You can create the support files as usual. Here we place all the output in the directory out:
java -DcreateStubs="true" weblogic.management.commo.WebLogicMBeanMaker -MDF MyA.xml -files out
Finally, you need to create the provider class com.oreilly.wlguide.security.iap.MyAProviderImpl, which was referred to in the ProviderClassName attribute. Example 17-9 lists this class in its entirety.
Example 17-9. The provider implementation
package com.oreilly.wlguide.security.iap; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.login.AppConfigurationEntry; import weblogic.management.security.ProviderMBean; import weblogic.security.spi.*; public final class MyAProviderImpl implements AuthenticationProvider, IdentityAsserter { private String description; // holds our description which we derive from MBean attributes public void initialize(ProviderMBean mbean, SecurityServices services) { MyAMBean myMBean = (MyAMBean)mbean; description = myMBean.getDescription( ) + " " + myMBean.getVersion( ); } public CallbackHandler assertIdentity(String type, Object token) throws IdentityAssertionException { if (type.equals("MyToken")) { byte[] tokenRaw = (byte[])token; String username = new String(tokenRaw); return new SimpleSampleCallbackHandlerImpl(username,null,null); } else throw new IdentityAssertionException("Strange Token!"); } public String getDescription( ) { return description; } public void shutdown( ) { } public IdentityAsserter getIdentityAsserter( ) { return this; // this object is the identity asserter } public AppConfigurationEntry getLoginModuleConfiguration( ) { return null; // we are not an authenticator } public AppConfigurationEntry getAssertionModuleConfiguration( ) { return null; // we are not an authenticator } public PrincipalValidator getPrincipalValidator( ) { return null; // we are not an authenticator } }
The most important methods are initialize( ) and assertIdentity( ). The initialize( ) method simply extracts some information from the MBean representing the provider and uses it to create the description. The assertIdentity( ) method is given two parameters, the type of the token and the token itself. We simply check that the token type is correct and map the token to the username. You could conceivably do a lot more here, such as validate the authenticity of the token for stronger security. The method must return a standard JAAS callback handler, which eventually will be invoked to extract the username (that is, only the NameCallback will be used). We use the callback handler that we defined in Example 17-4. Note that the identity asserter could have been an authenticator too, in which case it could populate the subject with usernames and groups belonging to the user. Because we are doing pure identity assertion, the corresponding methods simply return null.
Place this file and the callback handler in the out directory, and then issue the following command to create a packaged provider:
java weblogic.management.commo.WebLogicMBeanMaker -MJF myIAP.jar -files out
Copy this to the WL_HOME/server/lib/mbeantypes directory, and then reboot the server. Start up the Administration Console and navigate to the Security/myrealm Providers/Authentication node. In the list of available authenticators and identity asserters, you should find an option for "Configure a new MyA...". Selecting this option and clicking Create will configure the identity asserter. On the following tab you will notice that the support token type is set to MyToken and the active token to MyToken too. You will now have to reboot the server for this change to take effect.
If you rerun the client application, you will find that you will no longer get an unauthorized warning (assuming that jon is in the permission group mysecrole, which was granted access to the web resource). To further illustrate the point, you can try accessing a servlet or JSP page in this way, which has a call to request.getUserPrincipal( ). You will find that this call returns jon as you would expect.
So, here is a summary of what happens, as was illustrated in Figure 17-2:
Introduction
Web Applications
Managing the Web Server
Using JNDI and RMI
JDBC
Transactions
J2EE Connectors
JMS
JavaMail
Using EJBs
Using CMP and EJB QL
Packaging and Deployment
Managing Domains
Clustering
Performance, Monitoring, and Tuning
SSL
Security
XML
Web Services
JMX
Logging and Internationalization
SNMP