Sun Java Solaris Communities My SDN Account Join SDN
 
Enterprise Java Technologies Tech Tips

Securing Web Services Using WSIT, and Using Security Annotations in Enterprise Beans

 
In this issue
 
Welcome to the Enterprise Java Technologies Tech Tips for March 31, 2007. Here you'll get tips on using enterprise Java technologies and APIs, such as those in Java Platform, Enterprise Edition (Java EE).



This issue covers:

These tips were developed using the Java EE 5 SDK. You can download the SDK from the Java EE Downloads page .

You can download the sample archive for the tip Securing Web Services Using WSIT.

You can download the sample archive for the tip Using Security Annotations in Enterprise Beans.

Any use of this code and/or information below is subject to the license terms.

 
Securing Web Services Using WSIT
By Jiandong Guo  

Web Services Interoperability Technology (WSIT) is an implementation of open web services technologies that enables interoperability between Java EE and .Net. Built on Java API for XML Web Services (JAX-WS), WSIT addresses key aspects of web services interoperability such as reliable messaging, transaction handling, and security.

This tip focuses on WSIT's support for web services security. It shows you how to secure a web service using WSIT features. A sample application package accompanies the tip. The code examples in the tip are taken from the source code of the sample (which is included in the package).

Note that long URLs in the code examples in this tip have been placed on multiple lines for formatting purposes. See the sample application package for the actual code.

The sample uses the Java EE 5 SDK Update 3 Preview. You can download the Java EE 5 SDK Update 3 Preview from the Java EE Downloads -- Next Release (Early Access) page.

Security Support in WSIT

WSIT implements a number of security standards published by the Organization for the Advancement of Structured Information Standards (OASIS) consortium. These include

  • WS-Security – Provides the basic framework for message level security in web services.
  • WS-SecurityPolicy – Enables web services to specify security requirements to potential clients in an interoperable way.
  • WS-SecureConversation – Introduces secure sessions on top of WS-Security. It enhances overall security through key derivations and improves performance by avoiding repeated key exchanges in multi-message exchange scenarios.
  • WS-Trust – Specifies a framework for broker trust across different security domains.

The implementation of these standards enables WSIT to ensure secure communications between services.

In this tip you'll learn how to enable security for a web service with WSIT. You'll learn how to use the WS-Security support in WSIT so that a client can access a service in the same security domain. You'll also learn how to use the WS-Trust support in WSIT to access a service in a different security domain.

Creating a Security Policy

To enable security for a web service with WSIT, you need to create and attach a security policy to the Web Services Definition Language (WSDL) file for the service. The WSDL file is an XML file that describes a web service, its location, and the operations that the service exposes.

In general, there are two types of security policies you need to create to protect the web service: binding level policy and operational level policy. Let's look at the binding level policy first.

Binding Level Policy

A binding level policy specifies the types of service and client credentials needed to secure web services messages. The sample package that accompanies the tip provides a JAX-WS based web service, IFinancialService, and a WSDL file, PingService.wsdl, for that service. You can find the binding level policy for the sample in the WSDL file.

Here is part of the binding level policy used in the sample:

   <wsp:Policy wsu:Id="IFinancialService_policy">
    <wsp:ExactlyOne>
        <wsp:All>
            <sp:SymmetricBinding xmlns:
      sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
                <wsp:Policy>
                    <sp:ProtectionToken>
                        <wsp:Policy>
                      <sp:X509Token 
      sp:IncludeToken=
      "http://schemas.xmlsoap.org/ws/2005/07/
      securitypolicy/IncludeToken/Never">
                <wsp:Policy>
                      <sp:RequireDerivedKeys/>
                      <sp:RequireThumbprintReference/>
                      <sp:WssX509V3Token10/>
                </wsp:Policy>
                     </sp:X509Token>
                </wsp:Policy>
            </sp:ProtectionToken>
            ...
            
    </sp:SymmetricBinding>
           <sp:EndorsingSupportingTokens xmlns:
      sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
                <wsp:Policy>
                              <sp:X509Token 
      sp:IncludeToken=
      "http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/
      IncludeToken/AlwaysToRecipient">
                        <wsp:Policy>
                              <sp:RequireThumbprintReference /> 
                              <sp:WssX509V3Token10 /> 
                        </wsp:Policy>
                             </sp:X509Token>
                        </wsp:Policy>
           </sp:EndorsingSupportingTokens>
                                <wsap10:UsingAddressing/>
        </wsp:All>
    </wsp:ExactlyOne>
   </wsp:Policy>

The <wsp:Policy> element is the root element of the policy. The policy contains a number of policy assertions that specify the type of tokens supported for authentication and how messages should be encrypted and digitally signed.

Notice the two <sp:X509Token> elements. These are X509Token assertions. The first of these is under the ProtectionToken, which in turn, is under the SymmetricBinding. It asserts that an X509 certificate from the service is required to authenticate the service to the client and to protect the message. The second X509Token assertion is under the EndorsingSupportingToken. It indicates that an X509 certificate from the client is required for the client to authenticate to the service. In this case, there is a trust relationship between the client and the service. The service understands the client's identity as represented by its certificate.

In addition to supporting X509 certificate-based authentication, WSIT supports username/password and assertion-based authentication.

If the client and the service are in different security domains they have no direct trust relationship. In that case, you can use a trust authority called Security Token Service (STS) to authenticate the client. You can also use STS to issue a security token for the client to access the service, as specified in the WS-Trust standard. The STS is trusted by the client and the service. To use STS-based authentication with WSIT, you follow a similar process as you do for directly authenticating a client to the service. The binding policy looks almost the same as in the previous example. The one difference is in the second X509Token. You need to change that assertion to indicate that the client must call an STS first to get a security token. The security token is usually a Security Assertion Markup Language (SAML) token. Here's what the changed X509Token looks like:

   <sp:IssuedToken sp:IncludeToken=
    "http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/
    IncludeToken/AlwaysToRecipient">
       <sp:Issuer>
           <Address xmlns="http://www.w3.org/2005/08/addressing">
               http://localhost:8080/jaxws-sts/sts
           </Address>
           <Metadata xmlns=
              "http://schemas.xmlsoap.org/ws/2004/09/mex">
               <MetadataSection>
                   <MetadataReference>
                         <Address xmlns=
                         "http://www.w3.org/2005/08/addressing">
                          http://localhost:8080/jaxws-sts/sts
                         </Address>
                    </MetadataReference>
               </MetadataSection>
           </Metadata>
       </sp:Issuer>
   </sp:IssuedToken>

The first <Address> element in the changed X509Token specifies the endpoint of the STS. The second <Address> element, the one under the <Metadata> element, specifies the address for obtaining the WSDL of the STS using standard WS MetadataExchange (WS-MEX) protocols.

Operation Level Policy

Operation level policies are general used to indicate which parts of the messages for an operation are encrypted or digitally signed. Here is the operation level policy for the sample. You can find it in the PingService.wsdl file.

   <wsp:Policy wsu:Id="IFinancialService_Input_policy">
    <wsp:ExactlyOne>
        <wsp:All>
            <sp:SignedParts xmlns:
      sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
              <sp:Body/>
              <sp:Header Name="To" 
             Namespace="http://www.w3.org/2005/08/addressing"/>
              <sp:Header Name="From" 
             Namespace="http://www.w3.org/2005/08/addressing"/>
              <sp:Header Name="FaultTo" 
             Namespace="http://www.w3.org/2005/08/addressing"/>
              <sp:Header Name="ReplyTo" 
             Namespace="http://www.w3.org/2005/08/addressing"/>
              <sp:Header Name="MessageID" 
             Namespace="http://www.w3.org/2005/08/addressing"/>
              <sp:Header Name="RelatesTo" 
             Namespace="http://www.w3.org/2005/08/addressing"/>
              <sp:Header Name="Action" 
             Namespace="http://www.w3.org/2005/08/addressing"/>
            </sp:SignedParts>
            <sp:EncryptedParts xmlns:
      sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
            <sp:Body/>
            </sp:EncryptedParts>
        </wsp:All>
    </wsp:ExactlyOne>
   </wsp:Policy>

This policy indicates that the body and the specified headers need to be signed and the body needs to be encrypted.

Deploying the Web Service

You can build and deploy a WSIT security-enabled web service in the same way as a regular JAX-WS based web service. For information on how to build a JAX-WS based web service, see the tip Developing Web Services Using JAX-WS.

Creating the Client

After you deploy the web service, you can access it from a client program. The steps for building a client that accesses a JAX-WS based web service are described in the tip Developing Web Services Using JAX-WS. However to build a client that accesses a secured web service through WSIT, you need to configure security for the client.

Configuring Security Information For the Client

To configure security for a client you specify security-related information in a file named wsit-client.xml. This file, which contains a WSDL document, specifies a number of local security policies.

A standalone client, FinancialServiceClient.java, is bundled in the sample. Here is the security configuration for the client in the wsit-client.xml file:

   <wsp:Policy wsu:Id="ClientKeystorePolicy" 
     xmlns:sc="http://schemas.sun.com/2006/03/wss/client" 
     xmlns:wspp="http://java.sun.com/xml/ns/wsit/policy" 
     xmlns:scc="http://schemas.sun.com/ws/2006/05/sc/client" >
       <wsp:ExactlyOne>
           <wsp:All>
               <sc:KeyStore wspp:visibility="private" 
      location="$WSIT_HOME/xws-security/etc/client-keystore.jks"  
                  type="JKS" alias="alice" storepass="changeit">
               </sc:KeyStore>
               <sc:TrustStore wspp:visibility="private" 
                location="$WSIT_HOME/xws-security/etc/
                client-truststore.jks" 
                type="JKS" storepass="changeit" peeralias="bob">
               </sc:TrustStore>
               <tc:PreconfiguredSTS 
       xmlns:tc="http://schemas.sun.com/ws/2006/05/trust/client" 
       endpoint="http://localhost:8080/jaxws-sts/sts"
       wsdlLocation="http://localhost:8080/jaxws-sts/sts?wsdl"
       serviceName="SecurityTokenService"
       portName="ISecurityTokenService_Port"
           namespace="http://tempuri.org/">
               </tc:PreconfiguredSTS>
           </wsp:All>
       </wsp:ExactlyOne>
   </wsp:Policy>

This sample local policy specifies the certificate key stores as well as information about the local STS.

Running the Sample Code

A sample package accompanies this tip. To install and run the sample:

  1. If you haven't already done so, download the Java EE 5 SDK Update 3 Preview from the Java EE Downloads Page, and install it.

  2. Download the copyv3.zip file from the XWSS project utilities page and unzip the file. Then:

    • Set the AS_HOME system property to where you installed the Java EE 5 SDK Update 3 Preview.

    • Change to the directory where you unzipped the copyv3 files. Make sure that the value for the AS_KEYSTORE_PASSWORD property in the build.xml file is the correct keystore password for the SDK Application Server keystore.

    • Enter the following command:

          ant

      This copies the sample certificates to Application Server keystores for use with the sample.

  3. Set the WSIT_HOME system property:

    • Open the file <SDK_HOME>/domains/domain1/config/domain.xml in a text editor, where <SDK_HOME> is where you installed the Java EE 5 SDK Update 3 Preview.

    • Add the following JVM options:

    •         <jvm-options>-DWSIT_HOME=${com.sun.aas.installRoot}
              </jvm-options>
              <jvm-options>
              -Dcom.sun.xml.ws.transport.http.HttpAdapter.dump=true
              </jvm-options>       
              <jvm-options>
              -Dcom.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=true
              </jvm-options> 

  4. Download the sample package for the tip and extract its contents. You should now see the newly extracted directory as <sample_install_dir>/ws-trust, where <sample_install_dir> is the directory where you installed the sample package. For example, if you extracted the contents to C:\ on a Windows machine, then your newly created directory should be at C:\ws-trust.

  5. Change to the ws-trust/src/fs directory. In the build.properties file set java.home to the location of JDK on your system. Set glassfish.home to point to the installation directory of the Java EE 5 SDK Update 3 Preview.

  6. Start the Application Server in the SDK by entering the following command:

        <SDK_HOME>/bin/asadmin start-domain domain1

  7. Run the sample by entering the following command:

        ant run-sample

    You should see the following response:

        Acknowledgement : successfully deposited.

    You can view the message flows by examining the log file for the Application Server.

Running the Sample Code with STS

  1. In the PingService.wsdl file, comment out the following lines below the EndorsingSupportingToken:

        <sp:X509Token ...>
          ...
        </sp:X509Token>

    Uncomment the following lines:

        <sp:IssuedToken ...>
          ....
        </sp:IssuedToken>

    These changes indicate that instead of using your certificate to authenticate to the service, you need to use an STS to obtain an issued token.

  2. Change to the ws-trust/src/fs directory, and enter the following command:

        ant sts

    This will set up a sample STS.

  3. Enter the following command:

        ant run-sample

    You should see the following response:

        Acknowledgement : successfully deposited.

About the Author

Jiandong Guo is a staff engineer and a senior member of the Application Server Web Services Security Group. He has lead the development of implementations and solutions based on WS-SecureConversation and WS-Trust in the Tango project.

Back to Top 

Using Security Annotations in Enterprise Beans
By Shing Wai Chan  

Prior to Java EE 5, you could specify authentication and authorization information for web tier components as well Enterprise JavaBeans technology components, also know as enterprise beans, only in deployment descriptors. However Java EE 5 simplified things by incorporating security annotations. These annotations are specified in JSR 250: Common Annotations for the Java Platform .

The annotations simplify authorization for enterprise beans and for web components. The simplification is particularly significant for enterprise beans.

This Tech Tip shows you how to construct enterprise beans that are secured using security annotations. It also shows you how to access the enterprise beans from an application client.

A sample web application package accompanies the tip. The code examples in the tip are taken from the source code of the sample (which is included in the package).

A Simple Example

Let's begin by writing a simple enterprise bean, in this case, a stateless session bean named PingEjb, and include an authorization check.

   @Remote({Ping.class})
   @Stateless
   public class PingEjb implements Ping {

       @PermitAll
       public String pingPermitAll() {
           return "PingEjb: pingPermitAll";
       }

       @DenyAll
       public String pingPermitAll() {
           return "PingEjb: pingDenyAll";
       }

       @RolesAllowed({"staff"})
       public String ping() {
           return "PingEjb: ping";
       }
   }

Notice the three security annotations in PingEjb: @PermitAll, @DenyAll, and @RolesAllowed. Actually Java EE 5 introduced five security annotations. In addition to the three just mentioned, there is @DeclareRoles and @RunAs. All five security annotations reside in the package javax.annotation.security.

With @PermitAll, all security roles are allowed to invoke the associated method (or methods). With @DenyAll, no security roles are allowed to invoke the associated method. The @RolesAllowed annotation in PingEjb applies to the ping method. Here, only users with role "staff" can access this method.

A More Sophisticated Example

The previous example shows how easy it is to use @RolesAllowed to protect a method in an enterprise bean. Here is a more sophisticated example:

   @RunAs(value="staff")
   @DeclareRoles({"staff", "temporary"})
   @RolesAllowed({"ttrole"})
   @Stateless
   public class HelloEjb implements Hello {
       @Resource private SessionContext sc;
       @EJB private Ping ping;

       public String hello() {
           if (!sc.isCallerInRole("staff") &&
                  !sc.isCallerInRole("temporary")) {
               return "HelloEjb: hello";
           } else {
               throw new RuntimeException(
                      "of role staff or temporary");
           }
       }

       @RolesAllowed({"myrole"})
       public String ping() {
           return ping.ping();
       }
   }

There are two @RolesAllowed annotations in the HelloEjb example, one at the class level, another at the method level. The @RolesAllowed annotation at the class level applies to all business methods of the bean. But it can be overridden by a more specific method-level security annotation if it exists. Because there is no security annotation in the hello method, the method is subject to the class-level annotation @RolesAllowed({"ttrole"}). However, the ping method is subject to the method-level annotation @RolesAllowed({"myrole"}).

The @RunAs annotation specifies a role for subsequent invocations within a method call, but not for direct calls of the method. In the example, HelloEjb.ping() invokes the Ping.ping() method as a user with the role of "staff" rather than with the role of the logged-in user.

The @DeclareRoles annotation is used to define roles used by the component. In HelloEjb, the "staff" and "temporary" roles can be used by the call to isCallerInRole() inside the hello method. The isCallerInRole() method provides a more sophisticated security check than @RolesAllowed. In HelloEjb, only users in the "ttrole" role can access the hello() method. But users in the "staff" or "temporary" role cannot access the method.

Note that the following two examples are functionally equivalent in terms of security:

   @RolesAllowed("staff")
   public String dummy() {
       ...
   }

and:

   @DeclareRoles({"staff"})
   ...
   public String dummy() {
       if (!sc.isCallerInRole("staff")) {
           throw RuntimeException("Not authorized");
       }
       ...
   }

Write the Client

Now let's write a client Java technology program that uses the enterprise beans shown in the previous examples. Here's a snippet of the client code. You can find the complete code in the sample package:

   public class Client {
       private static @EJB Ping ping;
       private static @EJB Hello hello;

       public static void main(String arg[]) {
           System.out.println(
                  "Calling Ping.pingPermitAll(): ... " + 
                  ping.pingPermitAll());  

           try {
               System.out.println(
                      "Calling Ping.pingDenyAll(): ... " + 
                      ping.pingDenyAll());
           ...

           try {
               System.out.println(
                      "Calling Ping.ping(): ... " + 
                      ping.ping());

           ...

           System.out.println("Calling Hello.hello(): ... " + 
                  hello.hello());
           System.out.println("Calling Hello.ping(): .... " + 
                  hello.ping());
   ...

Notice that the client program accesses the enterprise beans in the same way as if there was no security specified. It looks up the beans using an @EJB annotation:

  private static @EJB Ping ping;
  private static @EJB Hello hello;

And it invokes the enterprise bean methods in the usual way:

  hello.hello();
  hello.ping()

Specify Security Information in Deployment Descriptors

Even though security annotations simplify authentication and authorization, you still need to specify some security-related information in deployment descriptors. The good news is that by using annotations and relying on default settings, the amount of security-related information you need to specify in deployment descriptors is greatly reduced. For applications that use authorization, you still have to set the security-role-mapping in deployment descriptors.

The Java EE environment uses roles for authorization. However in many operating system environments, users are associated with groups. The security-role-mapping provides a link between the concepts of user roles and principals or groups. The mapping allows you to associate a principal-name or group-name with a role-name. In a Java EE 5 application server implementation such as the Sun Java System Application Server in the Java EE 5 SDK, you define the security-role-mapping in the sun-application.xml file. Here is an example:

   <sun-application>
     <security-role-mapping>
       <role-name>ttrole</role-name>
       <group-name>ttgroup</group-name>
     </security-role-mapping>
     <security-role-mapping>
       <role-name>myrole</role-name>
       <principal-name>ttuser</principal-name>
     </security-role-mapping>
     <security-role-mapping>
       <role-name>staff</role-name>
       <principal-name>aprincipal</principal-name>
     </security-role-mapping>
     <security-role-mapping>
       <role-name>temporary</role-name>
       <principal-name>noone</principal-name>
     </security-role-mapping>
   </sun-application>

You need to define the mapping for each role used in the application. For the role in @RunAs, if no principal is defined in sun-ejb-jar.xml, the application server uses a principal from the security-role-mapping. Here is an example that defines in the sun-ejb-jar.xml file the run-as principal for the HelloEjb enterprise bean:

   <sun-ejb-jar>
     <enterprise-beans>
       <ejb>
         <ejb-name>HelloEjb</ejb-name>
         <principal>
           <name>aprincipal</name>
         </principal>
       </ejb>
     </enterprise-beans>
   </sun-ejb-jar>   

If you use a @RolesAllowed annotation, the application server provides a default for the <ior-security-config> element in the deployment descriptor. The default <ior-security-config> element is given an <auth-method> element value of username_password and a default realm for authentication. You can customize this by adding <realm> elements after the <security-role-mapping> element in the sun-application.xml file as follows:

   <realm>file</realm>

Run the Sample Code

  1. If you haven't already done so, download the Java EE 5 SDK from the Java EE Downloads page, and install it.

  2. Set the following environment variables:

    • JAVEE_HOME. This should point to where you installed the Sun Java System Application Server.

    • ANT_HOME. This should point to where ant is installed. Ant is included in the Sun Java System Application Server bundled with the Java EE 5 SDK. (In Windows, it's in the lib\ant subdirectory.)

    • JAVA_HOME. This should point to the location of JDK 5.0 on your system.

    Add $JAVA_HOME/bin, $ANT_HOME/bin, and $JAVAEE_HOME/bin to your PATH environment variable.

  3. Download the sample package for the tip and extract its contents. You should now see the newly extracted directory as <sample_install_dir>/ejb-secann, where <sample_install_dir> is the directory where you installed the sample package. For example, if you extracted the contents to C:\ on a Windows machine, then your newly created directory should be at C:\ejb-secann.

  4. Change to the ejb-secann directory and edit the build.properties file as appropriate. For example, if the admin host is remote, change the value of admin.host from the default (localhost) to the appropriate remote host. Also, make sure that the following properties are correctly specified:

    • admin.user. The ID of the administrator who starts and stops the domain.

    • admin.port. The http port number of administrative server.

  5. Update the value of AS_ADMIN_PASSWORD in the passwd file to the administrator password. Note that AS_ADMIN_USERPASSWORD is the password of the created user for the test case. If you update this, also update the corresponding value in the run target.

  6. Start the Application Server by entering the following command:

      <appserv_install>/bin/asadmin start-domain domain1

      where <appsrv_install> is where you installed the Application Server.

  7. Create a user, ttuser, in the default realm in the Application Server by entering the following command:

      ant create-user

  8. Build and deploy the sample. First enter the following command:

        ant build

    This compiles the enterprise bean and servlet web services classes and creates an ear file.

    Then enter the command:

        ant deploy

    This deploys the ear file in the Application Server.


  9. Run the client application to access the EJB. Enter the following command:

         ant run

    You should see results that look something like this:

          [exec] Calling Ping.pingPermitAll(): ... 
              PingEjb: pingPermitAll
          [exec] Expected failure for any call of Ping.pingDenyAll()
          [exec] Expected failure for direct call of Ping.ping()
          [exec] Calling Hello.hello(): ... HelloEjb: hello
          [exec] Calling Hello.ping(): .... PingEjb: ping

    The failure of the direct invocation of Ping.ping() is expected. That's because the user ttuser is in group ttgroup, which has roles myrole and ttrole, and the Ping.ping() method requires the role staff for authorization.

  10. You can undeploy the EJB ear file, by entering the command:

      ant undeploy

  11. After you undeploy the application, remove the user for testing by entering the following command:

      ant delete-user

About the Author

Shing Wai Chan is a member of the Sun Java Application Server and Java EE SDK development teams. He has been involved with Java EE security for the last few years.

Back to Top 

Developers Assistance

Need programming advice on Java EE? Try Developer Expert Assistance

2007 JavaOne Conference Registration is Live

Register today for the JavaOne conference and save $200. The program is new and expanded. Check the 2007 JavaOne Conference page for details.

Java Application Platform SDK Update 3 Preview

The Java Application Platform SDK Update 3 Preview is an early access release that adds the Sun Web Developer Pack and provides a new application server version, Sun Java System Application Server 9.1 Beta.

GlassFish V2 Download Contest

Download the GlassFish V2 Beta. Then enter the GlassFish V2 Beta Download Contest for a chance to win an Apple iPod Nano.

Rate and Review
Tell us what you think of the content of this page.
Excellent   Good   Fair   Poor  
Comments:
Your email address (no reply is possible without an address):
Sun Privacy Policy

Note: We are not able to respond to all submitted comments.