|
Welcome to the Enterprise Java Technologies Tech Tips for August 2007. Here you'll get tips on using enterprise Java technologies and APIs, such as those in Java Platform, Enterprise Edition (Java EE).
You can now read the Enterprise Java Technologies Tech Tips online as a web log.
This issue covers:
These tips were developed using an open source reference implementation of Java EE 5 called GlassFish, and the open source NetBeans IDE 5.5.1. You can download GlassFish from the GlassFish Community Downloads page. You can download the NetBeans IDE 5.5.1 from the NetBeans page.
You can download the sample archive for the tip Combining JavaServer Faces Technology, Spring, and the Java Persistence API.
You can download the sample archive for the tip Supporting Tokens and Issued Token Delegation in WSIT.
Any use of this code and/or information below is subject to the license terms.
Combining JavaServer Faces Technology, Spring, and the Java Persistence API
By Carol McDonald
An earlier Tech Tip, Using Java Persistence With JavaServer Faces Technology
examined a sample application that uses the Java Persistence API with the JavaServer Faces framework. This tip examines a sample application that uses JavaServer Faces technology, the Java Persistence API, and the Spring 2.0 framework.
A package that contains the code for the sample application
accompanies the tip. The code examples in the tip are taken from the source code of the sample (which is included in the package). The application in the tip is deployed on GlassFish, an open source reference implementation of Java EE 5. You can download GlassFish from the GlassFish Community Downloads page.
A Summary of the Technologies and Frameworks in the Sample Application
If you're not familiar with JavaServerFaces technology, the Java Persistence API, or Spring, here are brief descriptions:
-
JavaServer Faces Technology (often referred to as JSF) is a server-side user interface (UI) component framework for web applications. It simplifies the development of sophisticated interactive web UIs by providing configurable, reusable, extendable UI components, support for event handling, input converters and validators, a navigation model, a component rendering model, and a managed bean model for translating input events to server-side behavior.
-
The Java Persistence API provides a (plain old Java object) POJO-based persistence model for Java EE and Java SE applications. It handles the details of how relational data is mapped to Java objects, and it standardizes Object/Relational (O/R) mapping.
-
Spring is a lightweight, POJO-oriented, open source framework for developing Java enterprise applications. Spring does not reinvent application server functionality such as connection pooling, or provide an object-relational mapping layer. Instead it provides support for Inversion of Control (IoC), dependency injection, Aspect Oriented Programming (AOP), and an abstraction/services layer designed to make existing Java Enterprise application server technologies easier and more transparent to use.
The Sample Application
The sample application in this tip displays an online catalog of pets sold in a pet store.
A Listing Items page, list.jsp, displays pets in the catalog in a three-column table. Each row contains the name of a pet, its photo, and its price. A maximum of 10 rows are displayed on a page. The page also displays which items out of the total number of items in the catalog are displayed, such as "Item 31 ..40 out of 102". A user can click on links in the page to display the next 10 or previous 10 pets in the catalog, or display more details about a specific pet in the list.
This tip focuses on one feature of the sample application -- displaying the table of pets. You can learn how other features in the sample application are implemented by reading the blog entry Sample Application using JSF, Spring 2.0, and Java Persistence APIs.
Using JSF in the Application
If you examine the code in the list.jsp page, you'll notice that it uses JSF components. For example, the following dataTable component displays the table of pets:
<h:dataTable value='#{item.items}' var='dataTableItem'
border="1" cellpadding="2" cellspacing="0">
A dataTable component is useful when you want to show a set of results in a table. In a JSF application, the UIData component, the superclass of dataTable, supports binding to a collection of data objects. The UIData component does the work of iterating over each record in the data source. The HTML renderer for the dataTable component displays the data as an HTML table.
The value attribute in the dataTable tag points to a list of catalog items identified by the expression #{item.items}. The value is bound to the items property of a managed bean that has the managed bean name item.
The var attribute points to a single item in that list identified by dataTableItem. As the UIData component iterates through the list, each reference to dataTableItem points to the current item in the list.
JSF column components display the columns in the table. For
example, the following column component displays the price
column:
<h:column>
<f:facet name="header">
<h:outputText value="Price"/>
</f:facet>
<h:outputText value="#{dataTableItem.price}"/>
</h:column>
The column tag represent a column of data in a UIData component. As the UIData component iterates over the rows of data, it processes the UIColumn component associated with each column tag for each row in the table. The UIData component iterates through the list of items (item.items) and displays the dataTableItem.price. Each time UIData iterates through the list of items, it renders one cell in each column.
The dataTable and column tags use facet tags to represent parts of the table that are not repeated or updated. These include headers, footers, and captions.
Using Spring in the Application
One of the design goals of Spring is to easily integrate with other Java EE-based web frameworks and technologies. In fact, integrating Spring into JSF is simply a matter of configuring the Spring DelegatingVariableResolver in faces-config.xml, the file that lists the bean resources and navigation rules for a JSF application.
The Spring DelegatingVariableResolver enables a JSF application to register a custom replacement class for the standard JSF VariableResolver implementation. The standard JSF VariableResolver resolves variable references at evaluation time. The Spring DelegatingVariableResolver first delegates to the original resolver of the underlying JSF implementation, then to the Spring root WebApplicationContext. The WebApplicationContext is responsible for instantiating, configuring, and maintaining the lifecycle of a set of beans known as Spring beans. What this means is that you can configure Spring beans as managed properties of your JSF managed beans.
Here, for example, is a snippet of the faces-config.xml file for the sample application. In this example, the Spring bean named catalogService is configured as a managed property of the JSF managed bean class ItemController.
<application>
<variable-resolver>
org.springframework.web.jsf.DelegatingVariableResolver
</variable-resolver>
</application>
<managed-bean>
<managed-bean-name>item</managed-bean-name>
<managed-bean-class>
sessionpagination.ItemController
</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<property-name>catalogService</property-name>
<value>#{catalogService}</value>
</managed-property>
</managed-bean>
Notice that the ItemController's <managed-bean-scope> is defined as session. A JSF managed bean with session scope is stored in the session. This means that the bean's properties stay alive for the life of the HTTP session.
The catalogService is defined as a Spring bean, CatalogDAO, in the Spring configuration resource file WEB-INF/applicationContext.xml. Here is a snippet of that file:
<bean id="catalogService" class="service.CatalogDAO"/>
<bean name="itemController"
class="sessionpagination.ItemController">
<property name="catalogService">
<ref bean="catalogService"/>
</property>
</bean>
The element <property name="catalogService"> refers to the setCatalogService method in ItemController. In response, the Spring root WebApplicationContext injects the catalogService Spring bean into the catalogService property of the JSF managed bean ItemController. Here is a snippet of the ItemController source file:
public class ItemController {
private CatalogService catalogService;
public void setCatalogService(
CatalogService catalogService) {
this.catalogService = catalogService;
}
public DataModel getItems() {
if (model==null || index != firstItem){
model=getNextItems();
}
return this.model;
}
public DataModel getNextItems() {
model = new ListDataModel(
catalogService.getItems( firstItem,batchSize));
index = firstItem;
return this.model;
}
The getItems() method wraps a List of item objects returned from the catalogService in a DataModel. UIData supports data binding to a collection of data objects represented by a DataModel instance. The data collection underlying a DataModel instance is modeled as a collection of row objects that can be accessed by a row index. The APIs provide mechanisms to position to a specified row index, and to retrieve an object that represents the data that corresponds to the current row index.
Using the Java Persistence API With Spring
The Spring bean CatalogDAO uses an EntityManager Query object in the Java Persistence API to return a list of items. If you look at the source code for CatalogDAO , you'll notice that it annotates an EntityManager field with a @PersistenceContext annotation. This injects an entity manager into the Spring Bean in the same way that an Entity Manager is injected into an Enterprise JavaBeans Technology (EJB) session bean.
public class CatalogDAO implements CatalogService {
@PersistenceContext(unitName="PetCatalogPu")
private EntityManager em;
public List<Item>
getItems(int firstItem,int batchSize) {
Query q =
em.createQuery("select object(o) from Item as o");
q.setMaxResults(batchSize);
q.setFirstResult(firstItem);
List<Item> items= q.getResultList();
return items;
}
The createQuery method creates an instance of a Query class for executing a Java Persistence query language statement. The setMaxResults() method in Query sets the maximum number of results to retrieve, and the setFirstResult method sets the position of the first result to retrieve.
Item is an Entity class -- a typical Java Persistence entity object -- which maps to an ITEM table that stores the item instances. If you examine the source code for Item, you'll see that it meets the two requirements for an entity:
- The class is annotated with an
@Entity annotation.
- The primary key identifier is annotated with an
@Id annotation.
@Entity
public class Item implements java.io.Serializable {
@Id
private String itemid;
private String name;
private String description;
@OneToOne(cascade={CascadeType.PERSIST})
private Address address;
@ManyToOne
private Product product;
...
Because fields such as name and description are basic mappings from the object fields to columns of the same name in the database table, they don't have to be annotated. Notice that O/R relationships such one-to-one and many-to-one are specified in annotations for address and product.
The ItemController ManagedBean pages through the list of Items by maintaining the firstItem and batchSize attributes, and passes these as parameters to the CatalogService getItems(firstItem, batchSize) method, which gets the items for display in the table of pets.
Summary
This tip illustrated how Spring can be used with JSF and the Java Persistence API. Specifically, it showed how to use a JSF dataTable and DataModel to display a list of entities, where the entities are retrieved from a database using the Java Persistence API with Spring 2.0.
For more information on using Spring with JSF, see Chapter 15. Integrating with other web frameworks in the document "Spring - Java/J2EE Application Framework".
For more information about the sample application referenced in this tip, see the blog entry Sample Application using JSF, Spring 2.0, and Java Persistence APIs
Running the Sample Code
The sample code for this tip is available as a NetBeans project. You can build and run the sample code using the NetBeans IDE.
Setting Things Up
- Download and install NetBeans 5.5.1.
- Download and unzip Spring 2.1 (download the with dependencies version).
- Download and install GlassFish V1.
Alternatively you can use Sun Java System Application Server PE 9, Sun's binary distribution of GlassFish.
- Add the GlassFish server to NetBeans:
- Start NetBeans and switch to the Runtime window (Ctrl+5).
- Right-click the Servers node and select Add Server.
- Select Sun Java System Application Server.
- Browse to the location where you installed GlassFish and select Finish.
Create the Spring Library in NetBeans
- Open the NetBeans Library Manager (in the Tools menu) and create a new library called Spring.
- Add the following jars to the class path:
dist/spring.jar
dist/weaving/spring-aspects.jar
lib/jakarta-commons/commons-logging.jar
lib/log4j/log4j-1.2.9.jar .
- Set the Sources to the Spring
src directory.
- Set the Javadoc to the Spring
docs\api directory.
Open and Run the Sample code:
-
Download the sample code and extract its contents. You should now see the newly extracted directory as
<sample_install_dir>/SpringJPA, 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:\SpringJPA.
-
Start the NetBeans IDE. Click Open Project in the File menu and select the
SpringJPA directory you just unzipped. The SpringJPA application is a NetBeans Enterprise Application Project, which is actually comprised of two projects: SpringJPA and SpringJPA-war. SpringJPA-war is a Java EE Module of the SpringJPA project. SpringJPA-war generates the war file and SpringJPA generates the ear file which contains the war.
You will see a Reference Problems dialog when you open the project. That's because the Enterprise Application Project stores the absolute location to its J2EE Modules. To resolve the reference problems, you need to configure the location for the SpringJPA-war. However, first click Close in the dialog. The SpringJPA project will be in bold red meaning that it still has reference problems.
-
Right click the
SpringJPA project and select Resolve Reference Problems from the context menu. Use the Resolve Reference Problems dialog to map the SpringJPA-war module to its project, which you'll find is a subdirectory beneath the SpringJPA directory.
-
Right click the
SpringJPA project and select Open Required Projects. Now that the dependencies are correct, the SpringJPA-war project will always open with the SpringJPA project. However, there are additional references problems with the web module because it references the Spring jar files that are needed to build the project.
- Add the Spring library to the
SpringJPA-war. In the Project window under SpringJPA-war:
- Right click on Libraries and select Add Library.
- Select the Spring Library in the list, then click Add
Library.
-
Edit the properties in the
SpringJPA\setup\javadb.properties file, as appropriate.
- Start the Java DB database as follows:
- Select Java DB Database in the Tools menu.
- Select Start Java DB Server.
- Build the project as follows:
- Right click the
SpringJPA node in the Projects window.
- Select Clean and Build Project.
- Run the project as follows:
- Right click the
SpringJPA node in the Projects window.
- Select Run Project.
When you run the project, your browser should display the opening page of the JSF, Java Persistence API, and Spring 2.0 Sample Application (at http://localhost:8080/SpringJPA-war/).
About the Author
Carol McDonald is a Java Technology Evangelist at Sun Microsystems. As a software developer since 1986, Carol's experience has been in the technology areas of distributed network applications and protocols, including J2EE technology, XML, Internet/Intranet applications, LDAP, Distributed Network Management (CMIP,SNMP) and Email (X.400,X.500). Besides Java, Carol is also fluent in French and German.
Supporting Tokens and Issued Token Delegation in WSIT
By Shyam Rao
The March, 2007 Tech Tip Securing Web Services Using WSIT introduced Web Services Interoperability Technology (WSIT), 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. After introducing WSIT, the tip showed how to use the WS-Security support in WSIT so that a client can access a service in the same security domain. It also showed how to use the WS-Trust support in WSIT to access a service in a different security domain.
This tip expands on the earlier tip. It focuses on:
- Supporting tokens with sender-vouches SAML assertions
- Security Token Service (STS)-issued tokens
Used in combination, supporting tokens with sender-vouches SAML assertions and STS-issued tokens can be used to issue a token from an STS and delegate it to a user who is different than the web service client.
Note that an issued token may not be for the web service client directly, but for an another entity with which the web service client has a direct trust relationship. In this kind of scenario, the web service client acts as a proxy. The actual user logs into this proxy using his or her trusted credentials. The web service client then acts on behalf of the actual user to ask for a token from the STS, that is, the web service client trust STS. The STS issues a token, which is intended for use only by the actual user and not by the web services client.
In this tip, you'll learn about:
- Supporting tokens
- Sender-vouches SAML assertions
- WS-Trust extensions in WSIT
You'll also learn how to:
- Populate detail for an actual user in an SAML token to be issued from the requestor's SAML token.
- Develop a WS-Trust application with extensions.
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).
Supporting Tokens
In WS-Security, security bindings use tokens to secure a message exchange. The tokens, which represent security credentials such as an X509 certificates, are used to sign all or part of a message. This signature is called a message signature. A supporting token augments the claims provided by the token associated with a message signature.
There are four properties related to supporting tokens that can be referenced by a security binding: supporting tokens, signed supporting tokens, endorsing supporting tokens, and signed endorsing supporting tokens. Each of the these properties is specified by an assertion: SupportingTokens, SignedSupportingTokens, EndorsingSupportingTokens, and SignedEndorsingSupportingTokens, respectively.
For more information about supporting tokens, see the document Web Services Security Policy.
Sender-Vouches SAML Assertions
Security Assertion Markup Language (SAML) is an XML standard for exchanging authentication and authorization data between security domains. The authentication and authorization information is specified in SAML assertions which are communicated between an identity provider (a producer of assertions) and a service provider (a consumer of assertions). SAML assertions are attached to SOAP messages inside the security header of the message.
One of the supporting tokens you can specify in a security binding is an SAML token. A SAML token uses SAML assertions as security tokens. One type of SAML token is the sender-vouches SAML token. This token uses a method called a sender-vouches method to establish the correspondence between a SOAP message and the SAML assertions added to the SOAP message. The attesting entity provides the confirmation evidence that is used to establish the correspondence between the subject of the SAML subject statements in SAML assertions and SOAP message content.
For more information about the sender-vouches method, see the document Web Services Security: SAML Token Profile.
WS-Trust Extensions in WSIT
The technique used in this tip takes advantage of extensions that the WS-Trust implementation in WSIT exposes. These extensions make the WSIT STS implementation more transparent to users. You can implement these extension points if you want to implement an STS according to your business requirements.
Recall from the March, 2007 Tech Tip that an STS is a trust authority that you can use to issue a security token for a client to access a service if the client and the service are in different security domains and have no direct trust relationship.
The WS-Trust implementation in WSIT exposes the following extensions:
-
STSAuthorizationProvider. This extension enables runtime authorization of a requestor for a token to be issued by the STS. For example, you can create two users and in the provider implementation, include authorization logic that allows only one of the users to access the target service. You get the user name from the user's Subject principal.
STSAttributeProvider. This extension enables details about the requestor to be included in the token to be issued by the STS. If this extensions is implemented, the provider implementation returns an attribute that is included in a SAML assertion created by the STS. Users may have different identities for different services. You may have an identity mapping for different services. On the service side, you can supply a SAML token validator to check if the actual user id and attributes returned by the provider implementation are included or not. For example, suppose a user named alice maps to abcd if the target service is http:// ... and has attributes such as role and email address. In that case, the issued SAML token will include a user attribute such as the role of the user, which can be used for authorization to the service.
-
STSConfigurationProvider. This extension allows you to add or replace STS configuration properties beyond what is specified in the <STSConfiguration> element of an STS's WSDL. The STS WSDL specifies attributes for configuring an STS, such as the issuer for the STS, issued tokens from this STS must be encrypted or not, and issued keys from the STS must be encrypted or not. If you write a STSConfiguration element and you want to change values of few properties at run time, then you need to implement this provider.
Note that providers can plug in an extension implementation to an STS using the standard ServiceFinder with a file name such as com.sun.xml.ws.api.security.trust.STSAttributeProvider. This file contains the actual implementation class.
Populate Detail for an Actual User in an SAML Token To Be Issued By an STS
Suppose a client does not possess any certificates of its own, but only knows about the STS/Service certificate. In this case, the STS/Service requires the client to authenticate using a encrypted Username token. The Username token is a means of identifying the requestor by user name to the STS/Service.
Suppose too that the STS issues an SAML token to the client to
talk to a service. A SAML token uses SAML assertions as security tokens. There are three way in which STS could populate the user's claims in the issued SAML Token:
-
Access the user's claims in development code. This isn't of much use in real-time applications.
-
Implement the
STSAttributeProvider extension in WSIT's WS-Trust implementation to return the user's ID and attribute values specified at runtime.
-
Implement the
STSAttributeProvider extension in WSIT's WS-Trust implementation and use the user's claims specified in the requestor's SAML token. This adds the requestor's SAML token to the PublicCredential Set, that is, the set of public credentials held by the client's subject. If you use WSIT with StreamingSecurity, which is the default, the requestor's SAML is added as an XMLStreamReader. If you use WSIT with the DisableStreamingSecurity policy assertion, the SAML assertion is added as a DOM Element in the PublicCredential set. For more information about DisableStreamingSecurity, see the blog Improved XWSS implementation in WSIT Milestone 3.
You can implement the third approach by (1) adding a signed supporting SAML token in the STS WSDL -- this passes information about the user to the STS for the token to be issued, and (2) implementing the STSAttributeProvider extension.
For example, here is part of an assertion in an STS's WSDL to include a SAML token as a signed supporting token (some of the lines in this example have been broken to fit the width of the page):
<sp:SignedSupportingTokens
xmlns:sp=
"http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<wsp:Policy xmlns:
wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
<sp:SamlToken sp:IncludeToken=
"http://schemas.xmlsoap.org/ws/2005/07/
securitypolicy/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:WssSamlV11Token10></sp:WssSamlV11Token10>
</wsp:Policy>
</sp:SamlToken>
</wsp:Policy>
</sp:SignedSupportingTokens>
In the similar way, you can pass a SAML token, as a signed supporting token, and a Username/X509 token for authorization to a regular web service.
Here is part of a sample provider implementation of the STSAttributeProvider extension that populates the issued SAML token with the actual user's claims:
public class SampleSTSAttributeProvider implements
STSAttributeProvider{
public Map<QName, List<String>> getClaimedAttributes(
final Subject subject, final String appliesTo,
final String tokenType, final Claims claims){
final Set<Principal> principals = subject.getPrincipals();
final Set publicCredential = subject.getPublicCredentials();
final Map<QName, List<String>>
attrs = new HashMap<QName, List<String>>()
// You can iterate over the client's
// subject.getPublicCredentials() for the SAML token and
// retrieve the values(NameIdentifier, Attributes) from
// it and set it in the SAML Token to be issued. You can
// find a description of the actual implementation of
// this part later in this TechTip. You can also find
// this code in the sample package
return attrs;
}
}
You may also implement the STSAuthorizationProvider extension and the STSAttributeProvider extension to check a web service client's authorization claims for the token to be issued.
Developing a WS-Trust Application With Extensions
Let's create an application that uses the technique highlighted in the "Populate Detail for an Actual User in an SAML Token" section, that is, the technique that implements the STSAttributeProvider extension and uses the user's claims specified in the requestor's SAML Token. Let's develop the application using the NetBeans IDE. You can download the NetBeans IDE from the NetBeans site.
To build this application, you:
-
Include a signed supporting SAML token in an existing NetBeans security profile called "Username Authentication with Symmetric Keys".
-
Implement the
STSAttributeProvider extension to populate the actual user's name identifier and attributes in an SAML token to be issued from the requestor's SAML token.
-
Plug in the
STSAttributeProvider extension implementation to an STS.
-
Implement an SAML handler for the client.
-
Get the actual user's username and password.
To include a signed Supporting SAML token in the existing NetBeans profile "Username Authentication with Symmetric Keys":
-
Create an STS Project using NetBeans.
-
Right click on the project. Select Edit Web Services Attribute. Then select the Username Authentication with Symmetric key security mechanism. In response, the following WSDL is generated (some of the lines in this example have been broken to fit the width of the page):
<sp:SignedSupportingTokens
xmlns:
sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<wsp:Policy
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
<sp:UsernameToken
sp:IncludeToken=
"http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/
IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:WssUsernameToken10/>
</wsp:Policy>
</sp:UsernameToken>
</wsp:Policy>
</sp:SignedSupportingTokens>
-
Edit the generated STS WSDL by adding a
SignedSupportingTokens assertion for a SAML token just below the existing SignedSupportingTokens assertions (some of the lines in this example have been broken to fit the width of the page):
<sp:SignedSupportingTokens
xmlns:
sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<wsp:Policy
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
<sp:UsernameToken
sp:IncludeToken=
"http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/
IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:WssUsernameToken10/>
</wsp:Policy>
</sp:UsernameToken>
</wsp:Policy>
</sp:SignedSupportingTokens>
<sp:SignedSupportingTokens
xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<wsp:Policy
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
<sp:SamlToken
sp:IncludeToken=
"http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/
IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:WssSamlV11Token10></sp:WssSamlV11Token10>
</wsp:Policy>
</sp:SamlToken>
</wsp:Policy>
</sp:SignedSupportingTokens>
To implement the STSAttributeProvider extension to populate the actual user's name identifier and attributes in an SAML token to be issued from the requestor's SAML Token:
-
Write a class that populates the issued Token's name identifier with the one present in requestor's SAML Token. Here is a code snippet for a class that does that using streaming security in WSIT (some of the lines in this example have been broken to fit the width of the page):
String name_identifier = null;
Iterator iterator = publicCredential.iterator();
while (iterator.hasNext()){
Object obj = iterator.next();
if (obj instanceof XMLStreamReader){
XMLStreamReader samlAssertion = (XMLStreamReader)obj;
try {
if(samlAssertion.getEventType()
!= XMLStreamReader.START_DOCUMENT)
StreamUtil.moveToNextElement(samlAssertion);
while(samlAssertion.getEventType()
!= XMLStreamReader.END_DOCUMENT){
if (samlAssertion.getLocalName()
.equals("NameIdentifier") ||
samlAssertion.getLocalName().equals("NameID")){
name_identifier = samlAssertion.getElementText();
}
samlAssertion.next();
}
} catch (XMLStreamException ex) {
System.out.println(
"Exception while reading SamlAssertion as XMLStreamReader : "
+ex);
}
}
}
if (name_identifier != null){
List<String> nameIds = new ArrayList<String>();
nameIds.add(name_identifier);
attrs.put(new QName("http://sun.com",
NAME_IDENTIFIER), nameIds);
}
-
Write a class that populates the issued Token's attributes with the attributes present in requestor's SAML Token. Here is a code snippet for a class that does that using streaming security in WSIT (some of the lines in this example have been broken to fit the width of the page):
String name_identifier = null;
Iterator iterator = publicCredential.iterator();
while (iterator.hasNext()){
Object obj = iterator.next();
if (obj instanceof XMLStreamReader){
XMLStreamReader samlAssertion = (XMLStreamReader)obj;
try {
if(samlAssertion.getEventType() ==
XMLStreamReader.START_DOCUMENT)
StreamUtil.moveToNextElement(samlAssertion);
while(samlAssertion.getEventType()
!= XMLStreamReader.END_DOCUMENT){
if (samlAssertion.getEventType() ==
XMLStreamReader.START_ELEMENT
&& samlAssertion.getLocalName().equals(
"Attribute")){
int attributeCount =
samlAssertion.getAttributeCount();
for (int i=0; i<attributeCount; i++){
if(samlAssertion.getAttributeLocalName(i).equals(
"AttributeName")
||samlAssertion.getAttributeLocalName(i).equals(
"Name")){
// Set up a dummy attribute value
final QName key = new QName("http://sun.com",
samlAssertion.getAttributeValue(i));
List<String> tokenRequestor =
new ArrayList<String>();
if(samlAssertion.hasNext()){
samlAssertion.next();
if (samlAssertion.getLocalName().equals(
"AttributeValue")){
tokenRequestor.add(
samlAssertion.getElementText());
attrs.put(key, tokenRequestor);
break;
}
}
}
}
}
samlAssertion.next();
}
} catch (XMLStreamException ex) {
System.out.println(
"Exception while reading SamlAssertion as XMLStreamReader : "
+ex);
}
}
}
To plug in the STSAttributeProvider extension implementation to
an STS:
-
Open the already-created STS project in NetBeans.
-
Create a two-level directory with name
META-INF/services under Source Packages.
-
Create a file with the name
com.sun.xml.ws.api.security.trust.STSAttributeProvider under the META-INF/services directory. The file contains the implementation class, fs.simple.sts.SampleSTSAttributeProvider, for the sample package.
-
Plug in the
STSAttributeProvider extension implementation to the STS using the standard Service finder mechanism.
To implement an SAML handler for the client:
-
Create a project for the client.
-
Download the SAML Callback Handler implementation from the XWSS project utilities page. To create a SAML Sender Vouches with Certificates, you can use the downloaded SAML Callback Handler, as is, with the package name
xwss.saml. But for a SAML Holder-of-Key, you need to edit two statements in the callback handler implementation:
// home must be the GlassFish install directory
String home = System.getProperty("WSIT_HOME");
// specify the client private key alias from your client
// keystore
String client_priv_key_alias="xws-security-client;
Note: For setting the Sender-Vouches (SV) SAML Token's subject with the actual user's username, you need to edit the methods createSVSAMLAssertion(), createSVSAMLAssertion20() and add a new constructor SamlCallbackHandler(String username) in the callback handler. You can see these changes in the client's project of the sample package.
-
Right click on client's Web Service References for the STS. Select the Edit Web Service Attributes option. Then select the WSIT Configuration tab. Expand Username Authentication and click on the Browse... button to get the SAML callback handler class.
To get the actual user's username and password:
- Open the already-created client project in NetBeans.
- Open the
index.jsp page and add the following lines of code:
<form action="FinancialServiceClientServlet" method="POST">
username : <input type="text" name="userName">
password : <input type="password" name="passWord">
<input type="submit" value="Log In">
</form>
Adding these lines enables the actual user to login to a web services client with a valid username and password. Here, the client acts as a proxy. After successful validation of the actual user to the web services client, the client asks for an issued token from the STS on behalf of the actual user. The web services client creates a SAML Token in which the subject's name identifier in the SAML assertion is populated with the actual user's login name. The STS then extracts the actual user's login name from the SAML Token in the request message and populates the issued SAML token's subject with that login name.
Running the Sample
The instructions for running the sample package are based on GlassFish Version 2 Release Candidate 1 and the NetBeans 5.5.1 IDE. The instructions assume that you have installed the WSIT plugin into NetBeans. For information on installing the WSIT plugin into NetBeans, see Web Services Interoperability Technology: How to Download and Install.
A sample package accompanies this tip. To install and run the sample:
-
If you haven't already done so, download GlassFish Version 2 Release Candidate 1, and install it.
-
Download the c
opyv3.zip file from the XWSS project utilities page and unzip the file. Then:
-
Download the sample package and extract its contents. You should now see the newly extracted directory as
<sample_install_dir>/ws-trust-ext, 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-ext.
-
There are three NetBeans projects in the extracted sample package:
- FinancialService: A service project
- FinancialServiceSTS: An STS project
- FinancialServiceClient: A client project
Open the three projects in the NetBeans editor.
-
Attach the registered application server to the three projects. To do this, right click on the project and select Properties. Then click on Run. Select server from the drop-down list.
-
Change the location of the keystore and truststore to
<GF_HOME>/domains/domain1/config/keystore.jks and <GF_HOME>/domains/domain1/config/cacerts.jks, respectively, where <GF_HOME> is where you installed GlassFish Version 2 Release Candidate 1. Do this for the service, STS, and client. To do this, select the WSIT Configuration tab in the web services attribute editor for each project's web services node. Then change the location of the Keystore and Truststore to the absolute path of the application server's keystore and truststore.
-
Add the following JVM option in the
domain.xml file in the <GF_HOME>/domains/domain1/config/ directory:
<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>
-
Start the application server by entering the following command:
<GF_HOME>/bin/asadmin start-domain domain1
-
Add an application server user. The user will be used as the username token for the request message to the STS. See Adding Users to GlassFish. for further information. Create a user with the following
username and password:
username : test
password : test
You can also create other usernames and password. But to do that, you need to edit the username and password in the client configuration file of the STS in the client's project.
-
Run the three projects:
-
Right click on the FinancialService project and select Run Project.
-
Right click on the FinancialServiceSTS project and select Run Project.
-
Right click on the FinancialServiceClient project and select Run Project.
You must run the FinancialServiceClient project last.
A browser window will open with a JSP page asking you to login with a username and password. You can enter any username and password -- the sample code does not have an implementation for validating the actual user. The objective of this page is to get the actual user's login id from the login page and put it in the subject of a created SAML token on the client side.
You should see the following response:
Balance = 1,000,000 Acknowledgement = successfully
deposited
You can view the message flows by examining the log file for the application server.
About the Author
Shyam Rao is a member of the Web Services Security Group. He has been involved with WS-Security, WS-Trust and WS-SecureConversation in the Tango project.
|