|
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
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:
- 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.
- 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.
- 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>
- 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.
- 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.
- Start the Application Server in the SDK by entering the
following command:
<SDK_HOME>/bin/asadmin start-domain domain1
- 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
- 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.
- Change to the
ws-trust/src/fs directory, and enter the
following command:
ant sts
This will set up a sample STS.
- 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
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
- If you haven't already done so, download the Java EE 5 SDK
from the Java EE Downloads page, and install it.
- 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.
- 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.
- 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.
- 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.
- 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.
- Create a user, ttuser, in the default realm in the
Application Server by entering the following command:
ant create-user
- 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.
- 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.
- You can undeploy the EJB ear file, by entering the
command:
ant undeploy
- 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.
|