Figure 9.10 Login page Figure 9.11 Login page after failed login Testing the Secure Application At this point, we have all the pieces required to test our secure application.. You should
Trang 1def user = request.subject.remoteUser[]
def groups = []+request.subject.groups[]
%>
<h1>Hello ${user}</h1>
<h2>Your group(s):</h2>
<ul>
<% groups.each { g -> %>
<li>${g}</li>
<% } %>
</ul>
<h2><a href="<%= getRelativeUri('/logout.gt')%>">Logout<a></h2>
</body>
</html>
The final page we need to add is a logout page It is always a good practice to provide a
means to enable your users to log out of an application Listing 9.9 creates a logout page You
often should do some housekeeping on the “user” Global Context and remove any session-like
variables that you do not want to persist into the user’s next access to the site This could include
sensitive data that should not be persisted or just as a means of good housekeeping
Listing 9.9 Logout Page (/public/logout.gt)
<html>
<head>
<title>Logout</title>
</head>
<body>
<%
zero.core.security.LoginService.logout();
%>
<h1>You have been logged out</h1>
<h2>Return to
<a href="<%= getRelativeUri(‘/index.gt’) %>">Home page</a>
</h2>
</body>
</html>
Trang 2Figure 9.10 Login page
Figure 9.11 Login page after failed login
Testing the Secure Application
At this point, we have all the pieces required to test our secure application Start the application,
and open your browser to the secure https port defined in the configuration file In the preceding
example, we had chosen port number 9443, so your URL is https://localhost:9443/ You should
be presented with our insecure main landing page without being prompted for a login, as shown
in back in Figure 9.4
Click the link to go to the secure page If everything is configured properly, you will be
redirected to the login page (see Figure 9.10) for user authentication On subsequent attempts to
access the secure page after logging in, you will have a valid browser token and will not need to
log in again To test out our failed login logic, enter either a bad username or wrong password,
and click Submit You will be returned to the login page and an appropriate error message will be
displayed, as seen in Figure 9.11 At this point, log in using your correct username and password
and verify that you are able to log in We now look at our secured page with information about
your user, as shown in Figure 9.12 To close out our test, click the logout link, which will put you
on the logout page shown in Figure 9.13 To verify that you are actually logged out, manually
enter the secured landing page URL: https://localhost:9443/my/index.gt You should be
redi-rected to the login page once again You’ve done it! A nice, clean user-secured application
Trang 3Figure 9.13 Logout page
Directory Server Configuration
Directory servers, which implement the Lightweight Directory Access Protocol (LDAP), are
probably the most common user repository in most enterprises A directory server consists of a
hierarchical database that stores user information or users’ profile information by a unique
distin-guished name (DN), which is equal to a path and through which an application is able to search
and retrieve information Based on this information, a directory server is an ideal ID source for
this application functionality Other than the WebSphere sMash configuration settings, you’ll see
that this is very similar to the solution we just built Although there are some syntax contortions
that are necessary to deal with LDAP, it is very effective in what it does
Namely, directory servers store user information with quick and secure identification
access Because many LDAP installations are unique in how they store and define the data
struc-ture, we can’t cover all situations Consult your directory server’s documentation and site
admin-istrator for more details about the required search and filtering constraints
We show a generic configuration and leave it as an exercise for you and your LDAP
admin-istrator to determine the proper arguments to access your site’s LDAP particulars
The first thing we need to do is enable the application for LDAP authentication After a user
is logged in, the only attributes we have by default are the unique user ID, which (depending on the
LDAP configuration) may be different than the user ID used to log in and the group memberships,
Figure 9.12 Secured landing page
Trang 4Table 9.2 User Authentication Variables
Unique user ID request.subject.remoteUser[]
Group membership request.subject.groups[]
as shown in Table 9.2 In almost any situation, you’ll want more information than what is provided
here, so we’ll also look to see how to obtain extended LDAP attributes for a given user
Let’s define the configuration settings to authenticate with the LDAP server Edit your
application’s zero.config file and add the information, as shown in Listing 9.10 You need to get
the actual LDAP values from your friendly system administrator
Listing 9.10 LDAP-Specific Configuration Settings
# -# LDAP Settings
# -/config/security/userservice/registryType="ldap"
# General LDAP Auth
/config/security/userservice/ldap += {
"jndiProviderUrl" : "ldap://ldap.acme.com:389/",
"ldapUserIdSearchFilterPattern" :
"(&(mail={0})(objectclass=inetOrgPerson))",
"ldapGroupSearchFilterPattern" : "(&(member={0})
(objectclass=groupOfNames))",
"ldapUserIdBaseDn" : "ou=people,o=acme.com",
"ldapGroupBaseDn" :
"ou=memberlist,ou=groups,o=acme.com"
}
# Token Support - Pick Simple or LTPA2
# SimpleToken support
/config/security/token/simple += {
"tokenExpiration": 120,
"ssoDomains" : ["acme.com"]
}
Trang 5# LTPAToken2 support
# Referenced key is relative to /config dir
#@include "security/token/ltpa2.config"{
# "keyImportFile": "myLTPA.key",
# "keyPassword" : "myPassword",
# "ssoDomains" : ["acme.com"]
#}
These are the only configuration items we need to add to the existing values in the security
sample application Everything else remains as it is Let’s take a quick walk-through of these
set-tings First, we need to set the security registry type to LDAP The second stanza defines the
LDAP server host, its port, and its query settings Again, the actual values here are dependent on
the server installation you want to use
Directory Server User Details
Next, we need to create a method to assist us in obtaining more details about the authenticated
user To do this, we’ll need to obtain the Groovy-Ldap package from the Apache project.3 To use
Groovy-Ldap, download and extract the package to a temporary directory Copy the included
JAR file to your project’s /lib directory Next, create a new groovy file in your application
named /app/scripts/helpers/ldap.groovy, and populate it with the code shown in Listing 9.11 You
will need to alter the LDAP-specific variables to match your environment A second and even
bet-ter approach would be to put these values into the application’s zero.config file and reference
the settings appropriately In this LDAP helper method, we establish a connection to the LDAP
server, set up a search query, and cache the results into the user context object for future use Be
watchful of the objects that are returned from these LDAP calls, as the query might return an
array of objects, and the values for the entries will be either a string or an array of strings It’s best
to first test the type of the values before using them For details on how to use Groovy-Ldap, and
to see how you can perform advanced searches, add, updates, and deletes, refer to the
documenta-tion on the Apache.org website
Listing 9.11 LDAP User Details Function
package helpers
def getUser() {
if ( ! user.me[] ) {
3 GroovyLdap: http://cwiki.apache.org/DIRxSBOX/groovy-ldap.html
Trang 6// Replace these with your site specific values
def ldapServer = ‘ldap://acme.com:389/ou=people,o=acme.com’
def ldapSearchFilter = ‘(&(objectClass=person)(uid={0}))’
def uid = request.subject.remoteUser[]
def ldap =
org.apache.directory.groovyldap.LDAP.newInstance( ldapServer )
def search = new org.apache.directory.groovyldap.Search()
search.filter = ldapSearchFilter
search.filterArgs = [ uid.toLowerCase() ]
def results = ldap.search(search)
if ( results != null && results.size() > 0 ) {
user.me = results[0]
logger.INFO{ "Me: ${user.me[]}" }
}
}
return user.me[]
}
Now we have everything we need to deal with our user, and all his personal attributes
Because we protected the /my URL in the initial configuration, let’s create an index.gt file there
and see what our LDAP server can tell us about our user Place the code shown in Listing 9.12
into /public/my/index.gt
Listing 9.12 Enhanced User and Group Details Page
<%
def uid = request.subject.remoteUser[]
def groups = ( []+request.subject.groups[] ).sort()
def user = invokeMethod(‘helpers/ldap.groovy’,’getUser’, null)
%>
<table border='1' width="100%" cellspacing='0px' cellpadding='0px'>
<tr>
<th colspan='2'><h2>Hello ${uid}</h2></th>
</tr>
<tr>
<th><h2>Your LDAP details</h2></th>
<th><h2>Your Groups</h2></th>
</tr>
<tr>
<td>
Trang 7<table border='1' width='100%'>
<% user.keySet().sort().each { key -> %>
<tr><td>${key}</td><td>${user[key]}</td></tr>
<% } %>
</table>
</td>
<td>
<ul>
<% groups.each { g -> %>
<li>${g}</li>
<% } %>
</ul>
</td>
</tr>
</table>
The code should be self-explanatory, but we simply pull out our user’s unique ID, his
groups, and the user object from our helper method Then we sort the keys of the user object to
improve readability and dump its contents Finally, we dump the groups as well The results are
rather voluminous and potentially confidential in nature, depending on your LDAP’s
configura-tion You have to try this yourself to see the results—when I ran this on myself at my company,
there was little that I’d be comfortable showing the world Run this sample application using
your own companies’ LDAP server, and examine the details maintained about you as an
employee
OpenID Configuration
One of the newer and more exciting authentication concepts is called OpenID It is a
decentral-ized process, where essentially any service provider can act as an trustee for user authentication
Although the concept is that you would have a single login identity that would work across
the Internet, its common to have several different identities spread across different service
providers You actually might already have an OpenID—many social sites such as Facebook,
Google, and Yahoo!, to name a few, also act as OpenID providers
To find out if you have an ID, or want to create one, point your browser to www.openid.net
Here, you can see which sites support OpenID, as well as create your own personal OpenID You
then can build an OpenID-based WebSphere sMash application and impress all your friends
As with other authentication schemes, we have a little configuration setup that we need to
make, and then it is simply a matter of creating a custom Login form First, we need to add a
dependency to the zero.security.openid module into the project To do this in AppBuilder, select
Trang 8the Dependencies tab, click the Add button, search for “opened,” and click Add In Eclipse, open
the project’s /config/ivy.xml file, click Add, search on zero:zero.security.openid, and click OK
Additionally, we need to enable security to our zero.config and define a few OpenID-specific
settings, as shown in Listing 9.13 This is a fairly generic setup, and specific details can be found
in the Project Zero documentation under the security section of the developer guide
Listing 9.13 OpenID Security Configuration
# -# OpenID Settings
# -/config/security/userservice/registryType="openid"
# specify the OpenID loginPage and associated login related
# configuration
@include "security/openidLoginURL.config" {
"openidLoginPage": "/openidlogin.gt",
# [optional] require these openid profile entries
"sregRequired": ["email","nickname","gender"],
# optionally send date of birth, full name, country,
# timezone and language if available
"sregOptional": ["dob","fullname","country","timezone","language"],
# URL describing how application will leverage simple registration
# required and optional attributes
"sregPolicyURL" : "http://www.projectzero.org",
"allowedOpenidDomains" : ["myopenid.com"]
}
#specify the security constraints
@include "security/openidAuthentication.config"{
"conditions": "/request/path =~/my(/.*)?"
}
OpenID also supports standard profile information that may be optionally requested by
consuming applications This is meant to provide a better web experience and not to expose any
confidential user information Most OpenID providers allow the entry of one or more profile sets
Trang 9Table 9.3 OpenID Profile Fields
OpenID Profile
Fields
Description
nickname User’s preferred nickname
email User’s email address
fullname User’s full name
dob User’s data of birth (YYYY-MM-DD) Zeros may be used for
non-revealed values, such as birth year, which might result in something similar to: 0000-07-04
gender User gender: either “M” for male, or “F” for female
postcode UTF-8 string-free text that should conform to the end user’s.
country The end user’s country of residence in standard two-digit country code
format
language End user’s preferred language
timezone ASCII string from TimeZone database For example: “Europe/Paris” or
“America/Los_Angeles.”
Securing Outbound Connections
Up until this point, we have been discussing how to secure connection requests from external
browsers or applications coming into your applications services
Let’s shift gears a bit and deal with securing outbound connections to other services
Typi-cally, you will be using the Connection class to make outgoing connections These can be normal,
insecure HTTP-based calls, and authenticated requests to secured HTTPS-based services The
solution here is to pre-define the secure connections in the zero.config file, including the
required username and password; then we can make the calls as needed from our application code
Start by defining your secure outbound connection(s) in the zero.config file, as shown in
Listing 9.14 Here, we define a connection URL pattern, and the user and password values You
can enter the password in clear text, but it’s better to obfuscate it using XOR encoding Even with
encoding, storing passwords in a configuration is a concern, and you should make an effort to
4 OpenID Simple Registration Extension: http://openid.net/specs/openid-simple-registration-extension-1_0.html
that you can specify to be used by consuming websites The currently defined profile fields are
listed in Table 9.3 More information can be found at the OpenID Simple Registration Extension.4
Trang 10Figure 9.14 Password encoding using AppBuilder console
ensure that this file is protected from unauthorized viewing To encode the password, just run the
zero encode {password} command from either the command line or from the console tab in
AppBuilder, as shown in Figure 9.14
Listing 9.14 Defining Outbound Connections in Configuration File
/config/connection/destinations += {
"https://crownjewels.acme.com/*" : {
"connection" : {
"protocol" : "https",
"config" : {
"userid": "wecoyote@acme.com",
"password":"<xor>Mj44Njwoby07"
}
}
}
}
After we have our outbound connection pattern defined, all we have to do is to set up a new
connection call to our desired service
WebSphere sMash compares the outbound connection URL with the pre-defined destination
patterns, and if a match is made, will automatically apply the appropriate user and password
creden-tials to the connection This keeps our credencreden-tials external to our application logic, which is always
a good thing Listing 9.15 shows a code fragment that makes a connection to the remote server