Showing posts with label weblogic. Show all posts
Showing posts with label weblogic. Show all posts

Friday, April 30, 2010

Hello World using the Weblogic SCA support in JDeveloper 11.1.1.3.0

This is just a brief introduction to the newly added Weblogic SCA support that we have in the latest release of JDeveloper. Weblogic SCA allows you to take POJO classes and expose them as Web Services or EJB externally and use Spring internally to connect services together. You can find more on WebLogic SCA in the primary documentation.

Before we get going you are going to need to download the "Spring & Oracle WebLogic SCA" extension for JDeveloper as we don't ship it with the product. So as per normal Help->Check for Updates and select the matching extension:

Once this is installed and JDeveloper has restarted we need to perform an extra step to install the Weblogic SCA extension in the copy of WebLogic that comes with JDeveloper. In the context of a project select Run -> Start Server Instance. Then on the WebLogic console, "http://localhost:7101/console", navigate to "Deployment" then select the "Install" action. You can find the shared library you need to install under %INSTALL_HOME%/wlserver_10.3/common/deployable-libraries/weblogic-sca-1.1.war.

You need to just accept the defaults in the wizard and once finished we are almost ready to start writing code. If your are interested in taking this a little bit further you should go to Preferences -> Extensions and enable the weblogic-sca and spring extension that provide extra monitoring capabilities. These require a server restart to fully enable.

Heading back to JDeveloper now we are going to create a new project and then configure that project for as a Weblogic SCA project that is going to be deployed as a war file. (See documentation for more on deployment options).

This will give you an empty web.xml, an empty spring-context.xml and a weblogic.xml that sets up the dependency on the WebLogic SCA library. The final file looks a lot like this, in a more complicate environment you might need to specify the library version.

<?xml version = '1.0' encoding = 'windows-1252'?>
<weblogic-web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.bea.com/ns/weblogic/weblogic-web-app http://www.bea.com/ns/weblogic/weblogic-web-app/1.0/weblogic-web-app.xsd" xmlns="http://www.bea.com/ns/weblogic/weblogic-web-app">
    <library-ref>
        <library-name>weblogic-sca</library-name>
    </library-ref>
</weblogic-web-app>

We are going to create a few java classes to illustrate not just the publishing of a POJO as a service but also the wiring up of internal components using Spring. First lets define the service, you need a class and interface for SCA:


// Interface

package hello;

public interface Hello {
    
    public String sayHello(String name, String langauge);
    
}

// Implementation

package hello;

import translation.Translator;

public class HelloImpl implements Hello {
    
    private Translator _translator;
    
    public void setTranslator(Translator t) {
        _translator = t;
    }
    
    public String sayHello(String name, String langauge) {
        String message = "Hello " + name;
        return _translator.translate(message, langauge);
    }
    
    
}

For the purposes of the demo we are going to have to provide this Translator interface along with an dummy implementation again in separate files. The implementation could be from a JAX-WS proxy; but this is wired up in a different way from what we are showing today.


package translation;

public interface Translator {
    
    public String translate(String text, String langauge) ;
    
}

// Dummy Implementation

package translation;

public class DummyTranslator implements Translator {

    public String translate(String text, String langauge) {
        if ("fr".equals(langauge)) {
            return text.replace("Hello", "Salut! ");
        }
        else if ("de".equals(langauge)) {
            return text.replace("Hello", "Guten Tag");
        }
        else {
           return "DT " + langauge + " " + text;
        }
    }
}

So we are going to need two entries in spring-context.xml, the first is to wire the dummy translator up to HelloImpl:

  <bean id="translator" class="translation.DummyTranslator" />
  <bean id="hello" class="hello.HelloImpl">
     <property name="translator" ref="translator" />
  </bean>

One difference you might notice from the previous spring extensions is that the bean names and class names are now connected up to the Refactoring framework. So you can "Rename", "Find Usages" and "Go to Declaration" on most of the entries in this file.

The next entry in this file exposes the HelloImp as a web service using the binding.ws elements. So the final spring-context.xml becomes:

<?xml version="1.0" encoding="windows-1252" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:lang="http://www.springframework.org/schema/lang"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:sca="http://xmlns.oracle.com/weblogic/weblogic-sca"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool-2.5.xsd http://xmlns.oracle.com/weblogic/weblogic-sca META-INF/weblogic-sca.xsd">
  <!--Spring Bean definitions go here-->
  
  <bean id="translator" class="translation.DummyTranslator" />
  <bean id="hello" class="hello.HelloImpl">
     <property name="translator" ref="translator" />
  </bean>
  
  <!--Service definitions -->
  
  <sca:service name="helloService" target="hello" type="hello.Hello">
    <binding.ws xmlns="http://xmlns.oracle.com/weblogic/weblogic-sca-binding"
                port="hello" uri="/hello" soapVersion="1.1" name="hello"
                location="HelloLocation"/>
  </sca:service>
  
  
</beans>

So you can right click and "Run" spring-context.xml and everything gets deployed to the local application server. The easiest way to test the service is View -> Application Server Navigator and then select the deploy service from the "Web Services" node.

And then you will see the test page for this service, just a quick overview but enough to get starting with this new area of functionality. Note that unlike full SOA this feature is part of the standard WebLogic license. Also that if you have SOA tooling installed you will see more integration with tools such as the composite editor which "knows" about Spring SCA beans.

Tuesday, September 15, 2009

Wssp1.2-2007-Https-ClientCertReq.xml required further configuration

Just a quick post to note a problem I found with the above mentioned security policy. This policy should enabled mutual or two-way https; but you will find that if you deploy this service to what appears to be a properly configured service that it will fail:

@WebService
@Policy(uri="policy:Wssp1.2-2007-Https-ClientCertReq.xml")
public class HelloTwoWay {
   public String sayHello(String name)
   {
      return "Hello " + name;
   }
}

You need another step compared with other https policies to have this work. You need to go to Servers -> [ServerName] -> SSL -> Advanced and under "Two Way Cert Behaviour" you need at least "Client Certs Requested". You can go for the enforced option if you want to use mutual everywhere; but in that case you can use the more general https policies so it doesn't really make sense.

Wednesday, August 19, 2009

WssConfiguration and JAX-WS

In weblogic you can have multiple security configurations for web services but in 10.3 JAX-WS could only make use of the "default_wss" configuration. There was an annotation to control this; but it only worked for JAX-RPC service. But in 10.3.1, aka AS11R1, it now works for JAX-WS as well. And for completeness here is an example on a JAX-WS service.

Monday, August 10, 2009

Reminder edocs.bea.com -> OTN migration

Just a quick reminder that on the 31st of August edocs.bea.com will be turned off; but the good news is that all of the documentation has moved to the current bea page on document section on otn. Specifically Weblogic Server 10.3 and Weblogic Server 10.3.1 that is part of AS11R1. Guess we need to get used to a new page layout!

Tuesday, August 4, 2009

New default password for the integrated weblogic server in JDeveloper R1

I guess it must be hidden in the help somewhere but it is not 100% clear that the default password for the integrated copy of weblogic server that comes with JDeveloper changed for R1. Rather than weblogic/weblogic it is now weblogic/weblogic1. Not a big change but enough to cause problems for some users.

Monday, July 20, 2009

Applying a template to the domain used for the Integrated WebLogic Server

JDeveloper provides you with pre-build install of weblogic and a bunch of useful templates for JRF etc. There are time though when you really need to apply a new template. (For example if you are doing Async web services).

This isn't a problem on windows as the domain is easy to find, $USER_HOME/Application Data/JDeveloper/system.X.X.X.X/DefaultDomain, but on Unix the location is under a "dot" directory, ~/.jdeveloper/system.X.X.X.X/DefaultDomain. This causes a problem for the graphical domain configuration wizard because it wont let you pick a domain that lives in a hidden directory. It also doesn't have a box where you can just type your path which is annoying.

The most obvious workaround is to use the configuration wizard in text only mode which gives you an old fashioned but workable UI. In this interface you can just type the path of the system directory when it asks you where your domains live. Don't fall into the trap I did of giving it the full location of the DefaultDomain, it will prompt you for this on the next page.

If you prefer you can also use wlst for the same work. Just issue a readDomain, then an addTemplate, updateDomain, then a final closeDomain.

Tuesday, May 5, 2009

What version of JAX-WS is my weblogic using

Useful to know if you want to download the source for debugging purposes. Take a look at the following path:

%BEA_HOME%/modules/glassfish.jaxws.rt*.jar

For example you might see a file named "glassfish.jaxws.rt_2.1.3.jar". This tells you that we are using 2.1.3 of JAX-WS (src). On my machine using internal builds I get "glassfish.jaxws.rt_1.0.0.0_2-1-4.jar" which tells me that I am using a patched version of the 2.1.4 build of JAX-WS (src). The latter might differ slightly from the published source; but probably not enough to worry about.

Monday, April 20, 2009

Generating the inferred deployment descriptors from annotations

The problem with annotation it that it is not possible to easily understand what is in a .jar/.war/.ear file. There is a little tool in weblogic appmerge that will generate the equivalent deployment descriptors. You simply use the -writeInferredDescriptors parameter.

java weblogic.appmerge -writeInferredDescriptors -output output.war input.war

Have to admit not played with it yet as I just picked this from an internal mailing list; but it seems like a good tip for corner cases.

Wednesday, February 11, 2009

Mistake in security policy worked example

After some investigation it turns out that I made a mistake in the code I included in my security policy worked example that would have stopped bi-directional encryption from working. (eg if you had added Wssp1.2-2007-EncryptBody.xml to my example) I have updated the previous posting with a note and the some alternative code.

Monday, February 9, 2009

JavaDoc for Weblogic 10.3

Just a quick post, you can find the javadoc for any weblogic specific classes here.

Thursday, February 5, 2009

Configuring JDeveloper to use a different install | domain of weblogic

One of the cool features in JDeveloper that only gets used now and then is the ability to configure JDeveloper to use a different install of weblogic when running internally. It could also be the case you need a custom configured Domain to support your application, for example when doing buffered web services.

To configure a server connection you need to go to Tools -> Preferences -> Run then Edit Server instances.

We are going to create a new configuration, so press the New button and you should see a screen as follows (you could of course just edit the default server configuration and this would affect the entire IDE rather than on a application by application basis):

Now by default JDeveloper assumes that you are try to connect to a remote weblogic instance. So you could just define a new connection to this server, big green plus top right, and you would be ready to go at this point. For the purposes of this blog we are going to assume that you want to let JDeveloper do the management of a server on the current machine. So we tick "Let JDeveloper manage...." and provide the location of the wls_server and where the domain in placed. You do still need to create a valid connection to where the server will start up to; but you obviously wont be able to test this server connection until after you have let JDeveloper start the instance.

The final step is to configure the listener port, in most cases you should just set this to what is configured in your domain:

So you can now assign this application server instance to a particular application in jdeveloper. First bring up the properties for the application:

Then select the custom configuration that we created before. Note you can create new configurations here also; but I showed the preferences way of doing things for completeness.

Now anything you Run/Debug/Profile/Test from inside of this application will use this server instance.

Wednesday, February 4, 2009

Where is policy configured on the weblogic console?

On common issue people have is finding the WS-Policy page on the web logic console. Once you have navigated to the web service you tend to think to click on the "Security" tab but find nothing. This is because WS-Policy, at least for JAX-RPC, can be used to configure reliability or mtom as well so the setting for these are under the configuration tab instead:

Once you get your head around the fact that policy is not just for security this kinda makes sense.

Monday, January 26, 2009

Schema Validation for JAX-WS

We had a very simple test case that confused myself and Alan, the replacement Alan not the old one, for a few minutes today. Our web service looked like this:

@WebService
public class Echo {
    public int echoInt (int i) {
        return i;
    }

}

But when we passed in invalid data to the web service the value of i was 0. So here is example message that JAX-WS will consume properly; but is obviously invalid:

<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://project1/">
   <env:Header/>
   <env:Body>
      <ns1:echoInt>
         <arg0>xxxx</arg0>
      </ns1:echoInt>
   </env:Body>
</env:Envelope>

It took us a while to remember that schema validation is optional, (See section 1.1 of the JAX-WS specification), and that there is no standard way to turn it on. The RI on the other hand does provide a way and it is as simple as sticking in a annotation. It also has some cool stuff for handling and ignoring specific errors which I find interesting; but I don't have a use case for yet.

import com.sun.xml.ws.developer.SchemaValidation;

@WebService
@SchemaValidation()
public class Echo {
    public int echo (int i) {
        return i;
    }
}

With our previous test data you now get a fault back:

<?xml version = '1.0' encoding = 'UTF-8'?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Body>
      <S:Fault xmlns:ns4="http://www.w3.org/2003/05/soap-envelope">
         <faultcode>S:Server</faultcode>
         <faultstring>com.sun.istack.XMLStreamException2: org.xml.sax.SAXParseException: cvc-datatype-valid.1.2.1: 'xxxx' is not a valid value for 'integer'.</faultstring>
         <detail>
            ...
         </detail>
      </S:Fault>
   </S:Body>
</S:Envelope>

Of course there are performance reasons why you might not want to validate the incoming message but it could make your service code much easier if you know it doesn't have to deal with invalid data. Also this is part of the RI so subject to change more than the -WS straight.

Friday, January 23, 2009

Weblogic Documentation Search

I wanted to set up a quick search page to only query the 10.3 weblogic documentation. Google site specific search has to be paid for; but it appears you get it for free if you do adsense so excuse the ads.

Wednesday, January 21, 2009

Security Policy Worked Example

Whilst it can be simple in concept many people find configuring security for web services to be something that is very daunting. Unfortunately, and particularly for JAX-WS, there is not yet a nice simple tutorial available that explains all the steps. This isn't that tutorial yet; but I hope to be able to walk through the process providing as much information as possible so as to become a starting point. (Also refer to this weblogic document on the web logic site)

As I work developing tools for weblogic I tend to have a new install every two or three days. For this reason this blog will be from the angle of configuring web logic security from a developers point of view. I will try to annotated the process where you would likely diverge in a production environment. Any commands run in this blog are run in the context of setDomainEnv command that you can find in your domain's "bin" directory. For JDeveloper users who want to configure the integrated domain you will find this in your ~/.jdeveloper/systemXXXXXX/ DefaultDomain/bin directory.

So for the purposes of this blog we are going to consider a simple stock trading application. We are going to pick one of the predefined weblogic policies to make our life easier. My code looks something like this:

@WebService
@Policy(uri = "policy:Wssp1.2-2007-Wss1.1-UsernameToken-Plain-X509-Basic256.xml")
public class BrokerService {

   ...
}

This policy has a bit of everything, user name tokens, encryption of said tokens and then signing of the whole kit and caboodle. A better match for this service in the real world would probably be to encrypt the entire message; but it wouldn't be such a good example so I am going to stick with this policy.

It is worth taking a look at the policy name as the naming convention used by web logic can tell you a lot about what is actually in the file. (For the content of the file take a look at my previous missive) "Wssp1.2-2007" tells you that the policy file contains assertions from the WS-SecurityPolicy 1.2 specification using the revised 2007 name space. "UsernameToken-Plain" tell you that the unecrypted text of the password token if passed in rather than a digest. "x509" for most cases we are talking RSA Public/Private key. Finally "Basic256" tell you which combinations of algorithm suite is being used. The last point takes us to the Wssp1.2 specification section 6.1 which has a table which explains what encryption and what key lengths are required for each suite.

One thing to look at in this document the asynchronous minimum key length, AKL, is 1024 bits this means that you cannot unfortunately make use of the DemoIdentity key store for a simple configuration; but instead need to create some new keys fortunately weblogic has some commands that make this much easier.

Before we get to this we need to take a quick diversion into the topic of trust. For this all to work the server has to be able to trust that the key combination used by the client to sign the message is a valid one. You have two choices for this, either add the client certificate to the server trust store directly or sign the client certificate with a certificate authority that is trusted by the server. The latter is more useful for distributed application as you don't have to worry about out of band key passing so we are going to use it in this example.

Luckily your weblogic instances comes with a demo CA, you can find the certificate and key for this in .../wlserver_10.3 /server/lib/CertGenCA.der and CertGenCAKey.der. This key doesn't appear to change between weblogic installations and is trusted by the default DemoTrust store. For this reason it is very very important you never have the DemoTrust store enabled in a production environment. Otherwise anybody can become trusted fairly easily; but the purposes of setting up a development environment it is really useful.

We are going to use the weblogic CertGen command that will generate keys of the correct key length and more importantly sign it with the demo CA we just mentioned. So we need a client cert/key pair to sign the outgoing message and the server certificate to encrypt the important parts. You are probably bored of me exposing now so lets actually run some commands:

java utils.CertGen -certfile ClientCert -keyfile ClientKey -keyfilepass ClientKey
java utils.CertGen -certfile ServerCert -keyfile ServerKey -keyfilepass ServerKey

The server cert doesn't really need to be signed by the CA; but it is easier to use the same command to save time. Note if you were doing this for a production system you probably want to us something more industrial like openssl to generate your keys as the weblogic documentation recommends. From this you end up with a bunch of .der and .pem files which we need to import into key stores, actually it will create new one for you if they don't exist, to make them easier to use from java, again using the weblogic helper command:

java utils.ImportPrivateKey -certfile ClientCert.der -keyfile ClientKey.der -keyfilepass ClientKey -keystore ClientIdentity.jks -storepass ClientKey -alias identity -keypass ClientKey
java utils.ImportPrivateKey -certfile ServerCert.der -keyfile ServerKey.der -keyfilepass ServerKey -keystore ServerIdentity.jks -storepass ServerKey -alias identity -keypass ServerKey

So now we get into the nitty gritty of configuring the server side of the equation. As mentioned before we are going to rely on the DemoTrust store so we only need to configure the server certificate and private key. Now the easiest way to do this is to make us of a script that comes with the standalone web logic install, for some reason it doesn't get installed with JDeveloper, and you can find the script in ../wlserver_10.3/samples/server/examples/src/ examples/webservices/ wss1.1/configWss.py or from the edocs site. So to configure the server we simply need to run the following command making sure you replace /.../ with the absolute path to the file in each case.

wlst /.../configWss.py weblogic weblogic localhost 7001 /.../ServerIdentity.jks ServerKey identity ServerKey

You can verify that this command has run properly by looking at the "Web Service Security" tab on your domain from the weblogic logic console. Note that the default_ww configuration is used for all web services unless you intimate otherwise. That part of the configuration is for a future blog.

So after restarting your server you can now create a client to invoke this service. The code needs to provide the client key and certificates for signing; the servers certificate so we can encrypt the message and finally something to provide the user name password tokens. For completeness here is the code I used to test this configuration:


import java.security.cert.X509Certificate;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.xml.ws.BindingProvider;
import javax.xml.ws.WebServiceRef;

import weblogic.security.SSL.TrustManager;

import weblogic.wsee.security.bst.ClientBSTCredentialProvider;
import weblogic.wsee.security.unt.ClientUNTCredentialProvider;
import weblogic.wsee.security.util.CertUtils;

import weblogic.xml.crypto.wss.WSSecurityContext;
import weblogic.xml.crypto.wss.provider.CredentialProvider;


//


brokerServiceService = new BrokerServiceService();
BrokerService brokerService =
brokerServiceService.getBrokerServicePort();

// Security stuff
//

// String constants to for server certificate, and client identity store

String serverCertFile = ".../ServerCert.der";
String clientKeyStore = ".../ClientIdentity.jks";
String clientKeyStorePass = "ClientKey";
String clientKeyAlias = "identity";
String clientKeyPass = "ClientKey";

// Create list of credential providers

List credProviders = new ArrayList();

// Create user name token provider

ClientUNTCredentialProvider unt =
 new ClientUNTCredentialProvider("weblogic", "weblogic");
credProviders.add(unt);

// Create a credential provider with the client indentity and the server side
// certificate

final X509Certificate serverCert =
 (X509Certificate)CertUtils.getCertificate(serverCertFile);
serverCert.checkValidity();

CredentialProvider cp =
 new ClientBSTCredentialProvider(clientKeyStore, clientKeyStorePass,
                                 clientKeyAlias, clientKeyPass,
                                 "JKS", serverCert);
credProviders.add(cp);

// Finally add the credential providers to the request context

Map requestContext =
 ((BindingProvider)brokerService).getRequestContext();

requestContext.put(WSSecurityContext.CREDENTIAL_PROVIDER_LIST,
                credProviders);

// Setup the TrustManager to verify the signature on the returned message

requestContext.put(WSSecurityContext.TRUST_MANAGER,
                new TrustManager() {
     public boolean certificateCallback(X509Certificate[] chain,
                                        int validateErr) {
         // Check that the server cert matches
         boolean result = chain[0].equals(
                    serverCert);
         return result;
     }
 });

// Invoke the service

BigDecimal bigDecimal = brokerService.getStockQuote("ORCL");
System.out.println(bigDecimal);

Now of course you need to copy the client keystore and the server certificate to the machine you are running the client from. This is okay as the client keystore with the private key is a secret only the client needs to know and the server certificate is public information.

So here is the general overview of the steps that the weblogic client will go through to send this message:

  • Generate a new aes256 symmetric key, encrypt using the servers certificate. (Think RSA public key) and included in the message
  • Encrypt the WS-Security UNT headers using the aes256 key and replace in message
  • Include the client certificate, which the server trust as has been signed by the demo CA
  • Create signature block with digest for each part of the message and the client private key. (The server can then verify this using the client certificate)

Although this is probably a bit too much detail lets look at an example message that the client could send to the server. I have tried to annotate the message so we can relate it to the configuration we have done do far.

>
<?xml version = '1.0' encoding = 'UTF-8'?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Header>
      <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis 
-200401-wss-wssecurity-secext-1.0.xsd" S:mustUnderstand="1">
      

      
         <ns1:EncryptedKey xmlns:ns1="http://www.w3.org/2001/04/xmlenc#" 
Id="u2KgDzrQ0fxzn776">
            <ns1:EncryptionMethod 
Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p">
               <ns2:DigestMethod xmlns:ns2="http://www.w3.org/2000/09/xmldsig#" 
Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
            </ns1:EncryptionMethod>
            <ns3:KeyInfo xmlns:ns3="http://www.w3.org/2000/09/xmldsig#">
            

            
               <wsse:SecurityTokenReference 
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" 
xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd" 
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" 
wsse11:TokenType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" 
wsu:Id="str_c8qqMexjG7qPtxHf">
                  <wsse:KeyIdentifier 
EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" 
ValueType="http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#ThumbprintSHA1">
+JvAr7erivisYq6D+P8HGAj0678=</wsse:KeyIdentifier>
               </wsse:SecurityTokenReference>
            </ns3:KeyInfo>




            <ns1:CipherData>
               <ns1:CipherValue>rFSQYWTxid4uY6noIglQTy3uPqzhO7/DeVT6apdp2afD5hzw7pgn2HGO
eYyd06gnveW772BEoS0qQqea/kayEmik6ZO0Lme9BjsEiGMOirM8cxp1
GH8ITQEOWX7ZyrruzJq3nbJtECSUGtxsdLh1+YdybfhgXVZ50zE4mGwT
jQc=</ns1:CipherValue>
            </ns1:CipherData>
            

            
            <ns1:ReferenceList>
               <ns1:DataReference URI="#M107teyC8vM4NGla"/>
            </ns1:ReferenceList>
         </ns1:EncryptedKey>
         

         
         <wsse:BinarySecurityToken 
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" 
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" 
EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0 #Base64Binary" 
ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" 
wsu:Id="bst_OICTd53HaizQ97WY">
MIICLTCCAdcCEIaJVGuuAUqDWjfqd+LqWc8wDQYJKoZIhvcNAQEEBQAweTELMAkGA1UEBhMCVVM
xEDAOBgNVBAgTB015U3RhdGUxDzANBgNVBAcTBk15VG93bjEXMBUGA1UEChMOTXlPcmdhbml6YX
Rpb24xGTAXBgNVBAsTEEZPUiBURVNUSU5HIE9OTFkxEzARBgNVBAMTCkNlcnRHZW5DQUIwHhcNM
DkwMTE5MTM0NjU5WhcNMjQwMTIwMTM0NjU5WjB3MQswCQYDVQQGEwJVUzEQMA4GA1UECBYHTXlT
dGF0ZTEPMA0GA1UEBxYGTXlUb3duMRcwFQYDVQQKFg5NeU9yZ2FuaXphdGlvbjEZMBcGA1UECxY
QRk9SIFRFU1RJTkcgT05MWTERMA8GA1UEAxYIdWtwNzkyNjYwgZ8wDQYJKoZIhvcNAQEBBQADgY
0AMIGJAoGBAKSbrv5XD1sjEZxg8LApKmot1T5EqYTEo1J60hwev+3rXZAgjwXaoRx7Z5A2Ln35x
W6CJbhymiV/INfsm93VoJWKoN8g1/cEidoXnNfO+H/6WQPcrTtRuq1X9FrmKYWOAsmkNjX7ohdw
dKtWo+3twNgm+GhPrXi1U830e5PgBVX9AgMBAAEwDQYJKoZIhvcNAQEEBQADQQA/1ucLAAwUE5C
efd7PRk2dPvNX+idsDL+wntiRk2tH5WIxKVKytT64G6wtwLM4q+X2XDBfBHtPkJB1JYFZMyTo
</wsse:BinarySecurityToken>



         <dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
            <dsig:SignedInfo>
            

            
               <dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
               <dsig:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
               <dsig:Reference URI="#Timestamp_2aUNpfbuj63zIItu">
                  <dsig:Transforms>
                     <dsig:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                  </dsig:Transforms>
                  <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                  <dsig:DigestValue>FBrjakBhUyh83bJ+qXPavE0HyS4=</dsig:DigestValue>
               </dsig:Reference>
               

               
               <dsig:Reference URI="#Body_1XLlJAczORkplAwo">
                  <dsig:Transforms>
                     <dsig:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                  </dsig:Transforms>
                  <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                  <dsig:DigestValue>FXY8sR0rX7j5AoF/emb89cPI+94=</dsig:DigestValue>
               </dsig:Reference>



               <dsig:Reference URI="#unt_jGt3DAGS0A5sKDXi">
                  <dsig:Transforms>
                     <dsig:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                  </dsig:Transforms>
                  <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                  <dsig:DigestValue>ZvEVCrm9DiSQCEA2lL6TsCGCUZA=</dsig:DigestValue>
               </dsig:Reference>
               
Client certificate signature
               
               <dsig:Reference URI="#bst_OICTd53HaizQ97WY">
                  <dsig:Transforms>
                     <dsig:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                  </dsig:Transforms>
                  <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                  <dsig:DigestValue>s8yt4r8aHbAJ9+MVEd1MkwDy5Dc=</dsig:DigestValue>
               </dsig:Reference>
            </dsig:SignedInfo>
            

            
            <dsig:KeyInfo>
               <wsse:SecurityTokenReference 
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" 
xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd" 
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" 
wsse11:TokenType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile- 1.0#X509v3" 
wsu:Id="str_gm2kU2SAnassqTl7">
                  <wsse:Reference URI="#bst_OICTd53HaizQ97WY"
 ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile- 1.0#X509v3"/>
               </wsse:SecurityTokenReference>
            </dsig:KeyInfo>
         </dsig:Signature>
         

         
         <ns1:EncryptedData 
xmlns:ns1="http://www.w3.org/2001/04/xmlenc#" 
Encoding="UTF-8" Id="M107teyC8vM4NGla" 
MimeType="text/xml" 
Type="http://www.w3.org/2001/04/xmlenc#Element">
         

         
            <ns1:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
            <ns1:CipherData>
               <ns1:CipherValue>
oUoMOMf12IAMEgZ01UuaHY+xYlKkYeaAIrZyuN5CEafQRco1HecGmaGcjtB5q
cNrRTNmrE9S18Z9K1oE6kwHKp3ZeYMp7KF/TbiqOwvKsy/+YpTQMS8gaqgbDN
XlRi0gG5/CZO6yqParDs//h3xvC9L1uazotzTGJofDWXaF/O8RC0qamD6yuoH
olV38na2mq28X0j44Eki4zJIkQg9q2ORj1juEE8RBt/zNuFKggThJrsmsixUj
AVRHXYA0exbwqUgWGoEEvw/AK4ZQooXfBXfIOsvPn4O4e515QpXhtM6cuqUEM
soGSIO/N+HT8oi4tOurXOAVCMZw6RonqBRvUPvLl2MQm3RIg4kBNFN7VIkqw4c
3jj+BbjGksKrmT3XX9jgypDDKJbW0OcOCTGYMhawQ2/wJMH/JazR55zwRLEzn8
8EIrLQ4SuGU7TN3pRNXdL3GdRxMZXF05mPT+6FLiSZ73kQLXxjlfQ9Bx4U3k2W
m+Pa0nMk0vb8Vj2DV5ZOB830f9C15jCIn7cvFZmYIdXSLOl1zrBJ1zGpdmVov
e1eKETKOyADGhWfE4Rt7mxPhLsUrhOlLIg4YMp+q3MIFEknoB8aBS0JPs9TSPvkQ2tk=
</ns1:CipherValue>
            </ns1:CipherData>
         </ns1:EncryptedData>
         <wsu:Timestamp 
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" 
wsu:Id="Timestamp_2aUNpfbuj63zIItu">
            <wsu:Created>2009-01-20T14:35:09Z</wsu:Created>
            <wsu:Expires>2009-01-20T14:36:09Z</wsu:Expires>
         </wsu:Timestamp>
      </wsse:Security>
   </S:Header>



   <S:Body 
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" 
wsu:Id="Body_1XLlJAczORkplAwo">
      <ns2:getStockQuote xmlns:ns2="http://webservice.stockbroker.com/">
         <ticker>ORCL</ticker>
      </ns2:getStockQuote>
   </S:Body>
</S:Envelope>

I will leave the analysis of the response message from the server as an exercise for the reader at this point; but I think you get the general idea of how the different parts of the configuration to hang together to create a message.

It is worth trying, as a sanity check, using a client key that is not trusted by the server. You can easily create such a key using the keytool command

keytool -genkey -keyalg RSA -keystore UnsignedClient.jks -storepass ClientKey -alias identity -keypass ClientKey -dname "CN=Client, OU=WEB_AGE, C=UK" -keysize 1024 -validity 1460

In this case when the client is run with the new keystore the server will response with an indignant response that the certificate could be verified. The stack trace you might see will look something like this the following, this could be fixed by adding the new client certificate to the server key store we configured earlier.

Exception in thread "main" javax.xml.ws.soap.SOAPFaultException: Security token failed
 to validate. weblogic.xml.crypto.wss.SecurityTokenValidateResult@137c90d[status: false]
[msg [
[
  Version: V3
  Subject: CN=Client, OU=WEB_AGE, C=UK
  Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5

  Key:  Sun RSA public key, 1024 bits
  modulus: 10367272402852596114462785541689739976823783564655877706471495047404251812...
  public exponent: 65537
  Validity: [From: Tue Jan 20 16:18:56 GMT 2009,
               To: Sat Jan 19 16:18:56 GMT 2013]
  Issuer: CN=Client, OU=WEB_AGE, C=UK
  SerialNumber: [    4975f970]

]
  Algorithm: [SHA1withRSA]
  Signature:
0000: 65 F7 E6 13 7F 47 50 20   F5 DA 01 BE 44 39 B1 5E  e....GP ....D9.^
0010: 0E A3 23 CD 39 95 BB 3E   D2 CD 1E B8 A2 3E FC 74  ..#.9..>.....>.t
0020: F3 06 78 3C 2D 43 D8 26   E9 A3 2F 24 3F C2 A2 FF  ..x<-C.&../$?...
0030: 10 2D E1 ED 09 34 7F E8   B8 48 04 38 DE 4E B3 D9  .-...4...H.8.N..
0040: 37 27 F1 42 74 C1 A9 0C   61 E7 23 7F 09 A1 2F F1  7'.Bt...a.#.../.
0050: EC 0B 10 40 F8 DD C1 39   62 92 0A 62 96 D2 F8 5F  ...@...9b..b..._
0060: EF AF E3 93 C4 3E 62 D7   2F 5A 78 65 54 BD 28 4B  .....>b./ZxeT.(K
0070: DF 34 55 7D 8C 10 05 5A   DB 91 D8 A0 46 65 C3 16  .4U....Z....Fe..

]]
 at com.sun.xml.ws.fault.SOAP11Fault.getProtocolException(SOAP11Fault.java:190)
 at com.sun.xml.ws.fault.SOAPFaultBuilder.createException(SOAPFaultBuilder.java:122)
 at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:119)
 at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:89)
 at com.sun.xml.ws.client.sei.SEIStub.invoke(SEIStub.java:118)
 at $Proxy30.getStockQuote(Unknown Source)
 at com.stockbroker.webservice.BrokerServicePortClient.main(BrokerServicePortClient.java:107)
Process exited with exit code 1.

So this blog should have shown you have to understand policy names, attach policy to a class, configure a server with the correct key stores, create a client and some understanding on how the bit relate to the structures you will find in the resultant soap message. As I say before this is not a definitive tutorial; but perhaps at least a starting point for a further investigation.

Update 11 February: If you have been using this example to try to perform two way encryption you you will find that the client will fail to decrypt the message. This is because of a mistake in my original code, it has a trust manager that only trusts the server certificate:

// Setup the TrustManager to verify the signature on the returned message

requestContext.put(WSSecurityContext.TRUST_MANAGER,
                new TrustManager() {
     public boolean certificateCallback(X509Certificate[] chain,
                                        int validateErr) {
         // Check that the server cert matches
         boolean result = chain[0].equals(
                    serverCert);
         return result;
     }
 });

The failure message from weblogic wasn't particularly useful so I spend a lot of time making sure that the keys in the messages lined up.

Exception in thread "main" javax.xml.ws.soap.SOAPFaultException: weblogic.xml.dom.marshal.MarshalException: weblogic.xml.crypto.wss.WSSecurityException: weblogic.xml.crypto.encrypt.api.XMLEncryptionException: Unable to resolve encryption key for weblogic.xml.crypto.encrypt.api.EncryptedType{keyInfo=null, cipherData=CipherValue: 0i8YETKYNv6PKD9nZqikDDIYpBrqrfDORnLK+nyJiY9HBaE462+v/PCG0NCbO4kqyotFGPqMExSCZ4hYOGtR4nWseqoAWD7Z64SPKfNYv0ugRhTcGIsJ8kya1eJFOzNfwRFPSaalRzLDQ8j+Rl7yiw==, id='null', mimeType='null', encoding='null', encryptionMethod=EncryptionMethod: algorithm = http://www.w3.org/2001/04/xmlenc#aes256-cbc;} 

The fix for this was to replace the trust manager with one that either trusts everything, and returns true, or checks the client certificate. It is better to got for the latter.

List certificate = CertUtils.getCertificate(clientKeyStore,
  clientKeyStorePass,
  clientKeyAlias, "JKS");

final X509Certificate clientCert =
  (X509Certificate)certificate.get(0);


...

// Setup the TrustManager to verify the signature on the returned message

requestContext.put(WSSecurityContext.TRUST_MANAGER,
                new TrustManager() {
     public boolean certificateCallback(X509Certificate[] chain,
                                        int validateErr) {
         // Check that the server cert matches
         boolean result = chain[0].equals(
                    serverCert) || chain[0].equals(clientCert);
         return result;
     }
 });

I did think about fixing the code in the blog and saying no more about it, but I feel that you can learn more from people's mistakes as well as they successes. Thanks for Chris Muir for working through this one with me.

Monday, January 19, 2009

Viewing the Contents of WLS Policies in JDeveloper

If you want to know what is behind each WLS policy then in JDeveloper the simplest bet is to deploy your application. If you want to have a quick read before you decide which one to use you can convince the application navigator to display all the policies. you simply need to check "Show Libraries" in the application navigator and go to the relevant package.....

You can then open the policy file you are interested in like you would any other...

Friday, January 16, 2009

Weblogic Server 10.3 documentation as PDFs

Just a quick like to the web logic documentation in PDF format I find them much easier to work with as a lot of seperate pages are joined into one big document. They are also easier to print out and write on, although as the web service security one is 150 odds pages long you might not want to do this for the sake of the trees.

Friday, December 19, 2008

Controlling what service a proxy uses at deployment time

This blog represents the non-policy part of my demo at UKOUG'08 this year. In that scenario we had a simple loan application that made use of a "mocked" version of a credit rating service during developement and a more real service for production. (Mocked up using the HTTP Analyzer in this case) This following screen grab gives you an idea of the project structure:

If we ignore the deploy directory for now we can take a look at the code in LoanApprover. The key points to notice here is the @WebServiceRef which injects the endpoint from the web.xml and the callout method to provision security. The important thing here is that the new WSDL might have different security policy; but it is possible to write the code fairly generically to take this into account because of the way that the web logic client auto-configures. In this case the mock service had no security and the production service had WS-Security with plain username/password.

@WebService 
public class LoansApprover {

    /**
     * Credit rating service injected from web.xml
     **/
    @WebServiceRef(name = "CreditRatingService")
    CreditRating creditRating;
    
    /**
     * @return Loan application with approval code if
     *   approved.
     */

    public LoanApprovalReponse approveLoan(LoanApplication la)  {

        LoanApprovalReponse approvalReponse = new LoanApprovalReponse();

        // Provision credentials
        //
        
        CredentialProvisioner.provisionPort(creditRating);

        // Start looking up credit rating 
        Response ratingReponse = creditRating.lookupRatingAsync(
           la.getSsid());
        
        // Retrieve any customer records
        // ...
        // Process Credit rating
        
        try {
            int creditRating = ratingReponse.get();
            if (creditRating > 30) {
                approvalReponse.setApprovalCode(
                    UUID.randomUUID().toString());
            }

        } catch (Exception e) {
            e.printStackTrace(); // Do nothing
        }
        
        return approvalReponse;
    }
}

So the @WebServiceRef uses a name resource, defined some where in JNDI. In this case it is in web.xml for an EJB it would be in ejb-jar.xml. Lets take a quick look, note that because we specify the service class name the deployer runtime code will work out to inject the port to save you a little bit of typing.

<?xml version = '1.0' encoding = 'windows-1252'?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5" xmlns="http://java.sun.com/xml/ns/javaee">

    ...    
    
    <service-ref>
        <service-ref-name>CreditRatingService</service-ref-name>
        <service-interface>com.somecreditrating.xmlns.rating.CreditRating_Service</service-interface>
    </service-ref> 
</web-app>

So in development the service class has all the hard coded references to the mock service deployed on localhost. For production we are going to change the WSDL that the proxy loads.

If you look at the project structure you will notice that there is an production.ear file in a rather novel directory structure. You will notice that the ear lives under the "app" sub dir and there is a "plan" directory. This structure will be created for you if you do something like change a policy on a deployed web service; but we need this all in place from the start. (Take a look at the weblogic documentation for more information on this structure)

Now you have to create a plan.xml, see this information on how to create one from the command line, but in the best Blue Peter tradition here is one I created earlier. The key parts are the variable definition and the assignment later on that uses xpath to create a new wsdl reference. Be carefull with the xpath expression as a space in the wrong place can cause the expression not to work. When deployed the proxy created for @WebServiceRef will read from this WSDL not the hard coded one.

<?xml version='1.0' encoding='UTF-8'?>
<deployment-plan xmlns="http://www.bea.com/ns/weblogic/deployment-plan" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.bea.com/ns/weblogic/deployment-plan http://www.bea.com/ns/weblogic/deployment-plan/1.0/deployment-plan.xsd" global-variables="false">
  <application-name>production</application-name>
  <variable-definition>
    <variable>
      <name>CreditRatingService</name>
      <value>http://www.somecreditrating.com/xmlns/rating?WSDL</value>
    </variable>
  </variable-definition> 
  <module-override>
    <module-name>production.ear</module-name>
    <module-type>ear</module-type>
    <module-descriptor external="false">
      <root-element>weblogic-application</root-element>
      <uri>META-INF/weblogic-application.xml</uri>
    </module-descriptor>
    <module-descriptor external="false">
      <root-element>application</root-element>
      <uri>META-INF/application.xml</uri>
    </module-descriptor>
    <module-descriptor external="true">
      <root-element>wldf-resource</root-element>
      <uri>META-INF/weblogic-diagnostics.xml</uri>
    </module-descriptor>
  </module-override>
  <module-override>
    <module-name>LoanApplication-LoanApprover-context-root.war</module-name>
    <module-type>war</module-type>
    <module-descriptor external="false">
      <root-element>weblogic-web-app</root-element>
      <uri>WEB-INF/weblogic.xml</uri>
    </module-descriptor>
    <module-descriptor external="false">
      <root-element>web-app</root-element>
      <uri>WEB-INF/web.xml</uri>
      <variable-assignment>
        <name>CreditRatingService</name>
        <xpath>/web-app/service-ref/[service-ref-name="CreditRatingService"]/wsdl-file</xpath>
        <operation>add</operation>
      </variable-assignment> 
    </module-descriptor>
    <module-descriptor external="true">
      <root-element>weblogic-webservices</root-element>
      <uri>WEB-INF/weblogic-webservices.xml</uri>
    </module-descriptor>
    <module-descriptor external="false">
      <root-element>webservices</root-element>
      <uri>WEB-INF/webservices.xml</uri>
    </module-descriptor>
    <module-descriptor external="true">
      <root-element>webservice-policy-ref</root-element>
      <uri>WEB-INF/weblogic-webservices-policy.xml</uri>
    </module-descriptor>
  </module-override>
  <config-root>D:\prom-demo\jdeveloper\mywork\LoanApplication\deploy\production\.\plan</config-root>
</deployment-plan>

Now the deployment plan dir also allows you to add files to the classpath. In this case we have a simple properties file that provides a username and password to be later configured as a credential provider. (Do consider storing your password somewhere more safe) For completeness lets just check out the code that provisions the proxy with the username and password token. Nothing very special there.

private static Properties passwordStore;

public static void provisionPort(Object port) {
    assert port instanceof BindingProvider;
    
    BindingProvider bp = (BindingProvider)port;
    
    // Get class and find the port name, for the moment 
    // lets just cheat
    //
    
    Class proxyPort = port.getClass();
    Class ifClass = proxyPort.getInterfaces()[0];
    String portName = ifClass.getName();
    
    
    // Do some clever lookup in a keystore
    //
    
    Pair usernamepassword =
        getCredentials(portName); 
    if (usernamepassword!=null) {

        ClientUNTCredentialProvider credentialProvider =
            new ClientUNTCredentialProvider(usernamepassword.getLeft().getBytes(),
                                            usernamepassword.getRight().getBytes());
        
        bp.getRequestContext().put(
            WSSecurityContext.CREDENTIAL_PROVIDER_LIST,
            Collections.singletonList(credentialProvider));

    }
    
    
}

private static Pair getCredentials(String portName) {

    if (passwordStore==null)
    {
        passwordStore = new Properties();
        URL password_properties = CredentialProvisioner.class.getResource(
                                      "password.properties");
        if (password_properties!=null) {
            try {
                passwordStore.load(password_properties.openStream());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    // Get the packed version of the string
    //
    
    String packedVersion = passwordStore.getProperty(portName);
    if (packedVersion!=null) {
    
        String splitVersion[] = packedVersion.split("::");
        assert splitVersion.length == 2;
        
        return new Pair(splitVersion[0],splitVersion[1]);
    }
    else {
        return null;
    }

}

Unfortunatelly you cannot deploy this special directory structure from within JDeveloper, so you need to go into the weblogic console, Deployments->Install, but as you can see from the screen grab it will recognize the app/plan combo as a directory you can deploy:

And that is that, you application will deploy, read the new WSDL and start to use the external service.

Thursday, December 18, 2008

Simple custom policy example using JAX-WS on WebLogic

So I little while ago someone asked me how to using a weblogic JAX-WS client to connect to a service that required plain text WS-Security username / token headers. It turns out that non of the default policies that come with weblogic will work in this configuration because they all have a sensible level of security applied. This doesn't help when trying to deal with an older system so in the end I modified the existing Https username token policy and came up with this file:

<?xml version="1.0"?>
<wsp:Policy
  xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
  xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200512"
  >
  <sp:SupportingTokens>
    <wsp:Policy>
      <sp:UsernameToken
        sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200512/IncludeToken/AlwaysToRecipient">
        <wsp:Policy>
          <sp:WssUsernameToken10/>
        </wsp:Policy>
      </sp:UsernameToken>
    </wsp:Policy>
  </sp:SupportingTokens>
</wsp:Policy>

Now there is a lot on the weblogic documentation site about configuring security; but it took a while to absorb into something that worked. (My fault I guess) The most convenient option is to place the policy file in the "policies" sub dir under WEB-INF. So you ended up with a project that looks kinda like this:

You web service can then just refer to the policy using the "policy:" url as you would the built in policies:

package project1;

import javax.jws.WebService;

import weblogic.jws.Policies;
import weblogic.jws.Policy;

@WebService
@Policies( { @Policy(uri = "policy:usernametoken.xml") } )
public class Hello {
    public String sayHello(String name) {
        return "Hello " + name;
    }
}

In JDeveloper at least you have to type this by hand because the custom policy button is very broken in the current released version of JDeveloper. This we hope to address at some point in the future. This is all that is required to set up the service to use the default credential providers. Check out the link above for more information on configuring this.

So if you need to actually invoke this service you can skip some steps if the policies are advertised in the WSDL. Fortunately they are by default so when the JAX-WS client starts it will parse the polices from the WSDL and set itself up to expect the correct configuration to work. Now you might expect BindingProvider username / password properties to be used here; but instead you need to configure a credential provider as follows. Note there is no reference to the policy here.

public static void main(String[] args) {
    helloService = new HelloService();
    Hello hello = helloService.getHelloPort();
    // Add your code to call the desired methods.


    List<CredentialProvider> credProviders =
        new ArrayList<CredentialProvider>();

    String username = "weblogic";
    String password = "weblogic";
    CredentialProvider cp =
        new ClientUNTCredentialProvider(username.getBytes(),
                                        password.getBytes());
    credProviders.add(cp);

    Map<String, Object> rc = ((BindingProvider)hello).getRequestContext();
    rc.put(WSSecurityContext.CREDENTIAL_PROVIDER_LIST, credProviders);


    hello.sayHello("Bob");

}

In some cases you might have a WSDL that for various reasons doesn't publish the policies as you get them through some other side channel. This is fortunately relatively easy to fix with the use of the ClientPolicyFeature as shown in the next code snippet:

public static void main(String[] args) {
    helloService = new HelloService();


    ClientPolicyFeature cpf = new ClientPolicyFeature();
    InputStream asStream =
        HelloPortClient.class.getResourceAsStream("usernametoken.xml");
    cpf.setEffectivePolicy(new InputStreamPolicySource(asStream));

    Hello hello =
        helloService.getHelloPort(
           new WebServiceFeature[] {cpf});

    // Add your code to call the desired methods.


    List<CredentialProvider> credProviders =
        new ArrayList<CredentialProvider>();

    String username = "weblogic";
    String password = "weblogic";
    CredentialProvider cp =
        new ClientUNTCredentialProvider(username.getBytes(),
                                        password.getBytes());
    credProviders.add(cp);

    Map<String, Object> rc = ((BindingProvider)hello).getRequestContext();
    rc.put(WSSecurityContext.CREDENTIAL_PROVIDER_LIST, credProviders);


    hello.sayHello("Bob");

}

And that is it, quite simple once I got used to the weblogic way of working.

Thursday, November 20, 2008

Insulting Error Messages?

I was noodling around with deployment plans in weblogic and wanted to just it was doing what I expected by making it fail. I was a little supprised when I saw this error message....

Until I remembered that I had change the URL for an outgoing web service to "http://idiot". The full text of the error message was java.net.UnknownHostException : idiot. But it did make me smile in this form.