 |
» |
|
|
 |
|  |  |
J2EE security is a complex topic. The following three topics
are described in this blueprint: Data Transportation—Secure Sockets Layer (SSL) is a widely used technology to
protect data transfer. SSL enablement methods for both the Apache
Web Server and JBoss Enterprise Application Server Platform are covered
in this section. Application Authentication—Symas Connexitor Directory Services (CDS) is a directory-service
solution based on OpenLDAP, Berkeley DB, Cyrus SASL and OpenSSL. It
provides an integrated authentication and authorization-based security
mechanism for enterprise applications managed and deployed in JBoss.
The directory server can store credential information and application
privileges about the users who are granted or forbidden access to
specific resources. In addition to controlling user identities, OpenLDAP
can control access based on other attributes such as network address,
transport, encryption strength, dynamic relationships, and so on.
Some applications or Web pages in specific JBoss applications, require
access only by authenticated users. Symas CDS can provide this authentication
mechanism by storing user credentials in the directory server. When
authentication starts, the JBoss Enterprise Application Server uses
the org.jboss.security.auth.spi.LdapLoginModule module to query the Symas CDS for the user's identity EJB Security—J2EE
specifications define a role-based security model for Enterprise Java
Bean (EJB) components. The security policies and mechanisms are provided
by the EJB Deployer. Business methods in EJB should not contain any
security logic. When certain business methods are invoked by an EJB
client, the EJB container identifies the role of the client and checks
the permissions to base the invocation on the security requirements
as configured in the EJB descriptor files.
Enabling HTTPS Support in Apache |  |
The mod_ssl module provides an SSL implementation that allows
Web applications running within the Apache Web Server to communicate
securely with their respective clients. Communication can still occur
over standard HTTP, if desired. To enable HTTP over SSL (HTTPS), perform the following steps: Run the shell script file /usr/bin/gensslcert, to create dummy ssl keys for mod_ssl. This tool copies
the /etc/apache2/ssl.crt/ca.crt file to /srv/www/htdocs/CA.crt and creates the following key files:
/etc/apache2/ssl.crt/ca.crt /etc/apache2/ssl.key/server.key /etc/apache2/ssl.crt/server.crt /etc/apache2/ssl.csr/server.csr
Edit the /etc/sysconfig/apache2 file by adding ssl to the APACHE_MODULES definition and SSL to the APACHE_SERVER_FLAGS definition. After completing the edits, the lines should look like the
following: APACHE_MODULES="... ssl ..." APACHE_SERVER_FLAGS="SSL" Create an SSL virtual host configuration file by copying
template file to perform the test: # cp /etc/apache2/vhosts.d/vhost-ssl.template
/etc/apache2/vhosts.d/vhost-ssl.conf Restart Apache by entering the following: # /etc/init.d/apache2
restart Perform the test by navigating to the Web site located
at: https://<YOUR_HOSTNAME>
Verify the certificate is displayed as in Figure 17: Enabling HTTPS Support in JBoss |  |
To enable HTTPS support in JBoss, perform the following steps
on the JBoss Enterprise Application Server: Generate the encryption key for HTTPS by entering the
following: # $JAVA_HOME/bin/keytool -genkey -keyalg RSA \ -keystore osms.keystore -validity 3650 Input a value for both the keystore and key password for mykey at the prompt. In the example, osmspass is used as the password. After the input is finished,
a key file named osms.keystore is created in
the current directory. Copy the keystore file to $JBOSS_HOME/server/default/conf by entering the following: # cp osms.keystore
$JBOSS_HOME/server/default/conf Open the file $JBOSS_HOME/server/default/deploy/jboss-web.deployer/server.xml and uncomment the SSL/TLS Connector section. Update
the keystore file location and keystore password in the server.xml file as follows: <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false"
keystoreFile="${jboss.server.home.dir}/conf/osms.keystore"
keystorePass="osmspass" sslProtocol = "TLS" /> |
where: Start the JBoss Enterprise Application Server by entering
the following: # $JBOSS_HOME/bin/run.sh -b 0.0.0.0 -c default Open a browser and navigate to the following URL: https://<YOUR_HOSTNAME>:8443 The security alert prompt is displayed as in Figure 18: Click Yes. The JBoss Enterprise Application
Server default main page displays as in Figure 19:
Application Authentication with the Symas CDS |  |
Configuring the CDS ServerInstall the appropriate version of Symas CDS and configure
the CDS Server by setting the directive suffix to dc=example,dc=com in the slapd.conf. The schema files, cosine.schema and inetorgperson.schema should be included in the slapd.conf file. Restart the CDS Server by entering the following: # /etc/init.d/cdsserver
restart Run the following command to add the example entries to
the CDS server: /opt/symas/bin/ldapadd -x -D
<ROOTDN> -w <ROOTPW> where: Input the following entries to stdin. Once you have completed
the entries, press CTRL+D . dn: dc=example,dc=com
objectClass: dcObject
objectClass: organization
dc: example
o: example
dn: dc=osm,dc=example,dc=com
objectClass: dcObject
objectClass: organizationalUnit
dc: osm
ou: osm
dn: uid=tomy,dc=osm,dc=example,dc=com
objectClass: inetOrgPerson
uid: tomy
sn: tom
cn: tom yan
mail: tom.yan@example.com
carLicense: sea4321
userPassword: tom
dn: uid=benw,dc=osm,dc=example,dc=com
objectClass: inetOrgPerson
uid: benw
sn: ben
cn: ben won
mail: ben.won@example.com
userPassword: ben
dn: cn=jmxRole,dc=osm,dc=example,dc=com
objectClass: groupOfUniqueNames
cn: jmxRole
uniqueMember: uid=tomy,dc=osm,dc=example,dc=com
dn: cn=ejbRole,dc=osm,dc=example,dc=com
objectClass: groupOfUniqueNames
cn: ejbRole
uniqueMember: uid=benw,dc=osm,dc=example,dc=com
|
Securing the JMX-Console Using Symas CDSPerform the following steps on the JBoss Enterprise Application
Server: Edit the file $JBOSS_HOME/server/default/conf/login-config.xml and add the following contents: <application-policy name="LdapRealm">
<authentication>
<login-module code="org.jboss.security.auth.spi.LdapLoginModule" flag="required">
<module-option name="java.naming.factory.initial">
com.sun.jndi.ldap.LdapCtxFactory
</module-option>
<module-option name="java.naming.provider.url">
ldap://[IP or Name of CDS Server]:389/
</module-option>
<module-option name="java.naming.security.authentication">
simple
</module-option>
<module-option name="principalDNPrefix">
uid=
</module-option>
<module-option name="principalDNSuffix">
,dc=osm,dc=example,dc=com
</module-option>
<module-option name="rolesCtxDN">dc=osm,dc=example,dc=com
</module-option>
<module-option name="roleAttributeID">cn
</module-option>
<module-option name="uidAttributeID">uniqueMember
</module-option>
<module-option name="matchOnUserDN">true
</module-option>
</login-module>
</authentication>
</application-policy> |
Edit the file $JBOSS_HOME/server/default/deploy/jmx-console.war/WEB-INF/web.xml and make the following changes: change <auth-constraint>
<role-name>
JBossAdmin
</role-name>
</auth-constraint> |
to <auth-constraint>
<role-name>
jmxRole
</role-name>
</auth-constraint><security-role> |
and, change <security-role>
<role-name>
JBossAdmin
</role-name>
</security-role> |
to </security-role> <security-role>
<role-name>
jmxRole
</role-name>
</security-role> |
 |  |  |  |  | NOTE: The jmxRole was previously created in “Configuring the CDS Server”.
The file is configured so the users in the group jmxRole have access to jmx-console. For users who are not defined in jmxRole or do not exist in the directory server, access
is denied. |  |  |  |  |
Edit the file $JBOSS_HOME/server/default/deploy/jmx-console.war/WEB-INF/jboss-web.xml and change the contents to the following:: <jboss-web>
<!-- Uncomment the security-domain to enable security. You will
need to edit the htmladaptor login configuration to setup the
login modules used to authentication users.
<security-domain>java:/jaas/jmx-console</security-domain>
-->
<security-domain>java:/jaas/LdapRealm</security-domain>
</jboss-web> |
If the JBoss Enterprise Application Server is running,
stop it and rerun by entering the following:  |  |  |  |  | NOTE: The username and password used with the shutdown.sh command
are the same as ones used to access to the jmx-console. |  |  |  |  |
# $JBOSS_HOME/bin/shutdown.sh
-u admin -p admin -S # $JBOSS_HOME/bin/run.sh
-b 0.0.0.0 -c default Open a browser and navigate to the website located at: https://YOUR_JBOSS_SERVER:8443/jmx-console The authentication window is displayed as in Figure 20: Enter benw for the username and ben for the password. Click OK. Because the user benw is not in
the jmxRole group, access to the page is denied
as displayed in Figure 21: Open another browser and navigate to the same website
as in Step 5. Enter tomy for the username and tom for the password. Because the user tomy is in the group jmxRole, which
is granted access to jmx-console, the jmx-console page is displayed
as in Figure 22:
Implementing EJB Invocation Security with Symas CDS |  |
All access information to EJB methods is defined in the standard
J2EE XML descriptor file ejb-jar.xml. The methods
that a specific role can invoke are described in the <method-permission> elements. When a user logs
in and tries to call a method in an EJB, the EJB container in JBoss
identifies the user's role by querying the CDS Server and checks to
see if the user has the appropriate permission, as defined in the
configuration file ejb-jar.xml. The following example implements an EJB with two public methods: hello()and helloWorld(). The security
for these two methods is configured in the ejb-jar.xml file. Verify that all steps from the previous section, "Application
Authentication with Symas CDS", passed without errors. Implement a simple session bean with two methods, for
the test. For this test, the following source code is for the bean
implementation class. You need to create the appropriate Home interface
and Remote interface for the session bean. /**
Bean implementation class for Enterprise Bean: Hello
*/
public class HelloBean implements javax.ejb.SessionBean
{
private javax.ejb.SessionContext mySessionCtx;
public javax.ejb.SessionContext getSessionContext(){
return mySessionCtx;
}
public void setSessionContext(javax.ejb.SessionContext ctx){
mySessionCtx = ctx;
}
public void ejbActivate(){
}
public void ejbCreate() throws javax.ejb.CreateException{
}
public void ejbPassivate(){
}
public void ejbRemove(){
}
public String hello(){
return "This is from hello!";
}
public String helloWorld(){
return "This is from helloWorld!";
}
} |
Compile the Remote interface, the Home interface, and
the implementation class from Step 2 by entering the following: javac Hello.java HelloBean.java HelloHome.java Create the directory META-INF, at
the same level as the *.class files generated
in Step 3. Create the file META-INF/ejb-jar.xml and insert the following contents:  |
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems,
Inc.//DTD Enterprise JavaBeans 2.0//EN' 'http://java.sun.com/dtd/ejb-jar_2_0.dtd'>
<ejb-jar>
<description>OSMS EJB Security Hello Application</description>
<display-name>Hello EJB</display-name>
<enterprise-beans>
<session>
<ejb-name>Hello</ejb-name>
<home>HelloHome</home>
<remote>Hello</remote>
<ejb-class>HelloBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Bean</transaction-type>
</session>
</enterprise-beans>
<assembly-descriptor>
<security-role><role-name>ejbRole</role-name></security-role>
<security-role><role-name>jmxRole</role-name></security-role>
<method-permission><role-name>ejbRole</role-name>
<method>
<ejb-name>Hello</ejb-name><method-name>hello</method-name>
</method>
<method>
<ejb-name>Hello</ejb-name><method-name>create</method-name>
</method>
</method-permission>
<method-permission><role-name>jmxRole</role-name>
<method>
<ejb-name>Hello</ejb-name><method-name>helloWorld</method-name>
</method>
<method>
<ejb-name>Hello</ejb-name><method-name>create</method-name>
</method>
</method-permission>
</assembly-descriptor>
</ejb-jar> |
 |
The META-INF/ejb-jar.xml file is configured
so the role ejbRole is permitted to invoke the hello()() method while jmxRole is permitted to invoke the helloWorld()() method. Create a file named META-INF/jboss.xml that contains the following contents: <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems,
Inc.//DTD Enterprise JavaBeans 2.0//EN' 'http://java.sun.com/dtd/ejb-jar_2_0.dtd'>
<jboss>
<security-domain>java:/jaas/LdapRealm</security-domain>
<enterprise-beans>
<session>
<ejb-name>Hello</ejb-name>
<jndi-name>osmsHello</jndi-name>
</session>
</enterprise-beans>
</jboss> |
The <security-domain> in the jboss.xml file should
be consistent with the application policy name for CDS Server authentication
in the $JBOSS_HOME/server/default/conf/login-config.xml file. Pack the directory META-INF and all *.class files into a JAR package by entering the following: # jar cvf hello.jar
META-INF Hello.class HelloBean.class \ HelloHome.class Deploy the JAR package to the JBoss Enterprise Application
Server by entering the following: # cp hello.jar $JBOSS_HOME/ server/default/deploy Check the JBoss output on the screen and verify that the
following messages appear: INFO [EjbModule] Deploying Hello
[ProxyFactory] Bound EJB Home 'Hello' to jndi 'osmsHello'
|
This means that the EJB has been deployed to JBoss successfully.
Implementing EJB Invocation Security from Standard EJB ClientsThis section provides the steps for invoking EJB methods from
standard EJB clients. Write a standard EJB client which contains the following
contents:  |
/*HelloClient.java*/
import java.util.*;
import javax.ejb.CreateException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import java.io.*;
import java.rmi.RemoteException;
import javax.security.auth.callback.*;
public class HelloClient
{
public static void main( String [] args ){
String userName = args[0];
String password = args[1];
System.out.println("Logging in user:" + userName);
final String authFile = "./ejbLogin.conf";
System.setProperty("java.security.auth.login.config", authFile);
MyCallbackHandler handler = new
MyCallbackHandler(userName,password);
try{
LoginContext lc = new
LoginContext("ejbLogin",handler);
lc.login();
System.out.println("Successfully logged in user:" +
userName);
}
catch (LoginException le){
System.out.println("Login failed");
le.printStackTrace();
return;
}
try{
Properties props = new Properties();
props.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
props.put("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces");
props.put("java.naming.provider.url", "localhost:1099");
Context context = new InitialContext(props);
Object lookupObj = context.lookup("osmsHello");
HelloHome home = (HelloHome)
PortableRemoteObject.narrow(lookupObj,
HelloHome.class);
Hello hello = home.create();
System.out.println("EJB method hello returns:" +
hello.hello());
}
catch(Exception e){
e.printStackTrace();
return;
}
}
}
class MyCallbackHandler implements CallbackHandler
{
private String username;
private String password;
public MyCallbackHandler(String username, String password){
this.username = username;
this.password = password;
}
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++){
if (callbacks[i] instanceof NameCallback){
NameCallback nc = (NameCallback)
callbacks[i];
nc.setName(username);
}
else if (callbacks[i] instanceof
PasswordCallback){
PasswordCallback pc =
(PasswordCallback) callbacks[i];
pc.setPassword(password.toCharArray());
}
else{
throw new UnsupportedCallbackException(
callbacks[i], "Unrecognized Callback");
}
}
}
} |
 |
In the code, the hello()() method is invoked and its return string is set to print to the console. Compile the EJB client by entering the following: # javac HelloClient.java Create an EJB login configuration file named ejbLogin.conf, save it in the same directory as HelloClient.class, and add the following contents: ejbLogin{
org.jboss.security.ClientLoginModule required;
}; |
Run HelloClient() with the user benw and password ben, by entering the following: # java HelloClient benw ben  |  |  |  |  | NOTE: Set the environment variable CLASSPATH so that
it contains the current working directory, the path of jbossall-client.jar, and the path to the EJB interface which you created in the previous
section. |  |  |  |  |
The following messages appear: Logging in user:benw
Successfully logged in user:benw
EJB method hello returns:This is from hello! |
Because the user benw has the
role of ejbRole in the CDS Server
and ejbRole is granted the privilege of invoking hello()() method, the EJB client can invoke the hello() ()method successfully. Run HelloClient() with the user tomy and the password tom by entering the following: # java HelloClient tomy tom The following messages appear: Logging in user:tomy
Successfully logged in user:tomy
java.rmi.AccessException: SecurityException; nested exception is:
java.lang.SecurityException: Insufficient method permissions,
principal=tomy, ejbName=Hello, method=hello, interface=REMOTE,
requiredRoles=[ejbRole], principalRoles=[jmxRole]
… |
Even though the user tomy exists
in the CDS Server, it does not have the role ejbRole and cannot invoke the hello()() method.
Implementing EJB Invocation Security from Web ApplicationsThis section provides the steps for invoking the EJB methods
from Web applications. Create a file named helloWorld.jsp in the current working directory, for example $EJB_TESTAPP_HOME, and add the following contents: <%@page contentType="text/html;charset=GBK"%>
<%@page import="java.io.*,
javax.naming.Context,
javax.naming.InitialContext"
%>
<html>
<body>
<%
try{
Context ctx = new InitialContext();
Object obj = ctx.lookup("osmsHello");
HelloHome home =(HelloHome)javax.rmi.PortableRemoteObject.narrow(obj,
HelloHome.class );
Hello helloWorld = home.create();
out.println("helloWorld method returns:" + helloWorld.helloWorld());
}
catch (Exception e){
out.println(e.toString());
}
%>
</body>
</html> |
 |  |  |  |  | NOTE: In the helloWorld.jsp file, helloWorld()() is invoked and its return string is output on the Web page. |  |  |  |  |
Create the directory WEB-INF at the
same level as the file helloWorld.jsp. Create a file named WEB-INF/web.xml, in the current working directory, and add the following contents:  |
<?xml version="1.0"?>
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<ejb-ref><ejb-ref-name>ejb/Hello</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<home>HelloHome</home><remote> Hello</remote>
<ejb-link>Hello</ejb-link>
</ejb-ref>
<security-constraint>
<display-name>Constraints of osms Security Environment
</display-name>
<!-- URI security patterns and the HTTP methods to protect on them. -->
<web-resource-collection><web-resource-name>
Protected Admininistration Console Resources
</web-resource-name>
<url-pattern>/helloWorld.jsp</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<!-- Anyone with these roles may enter this area. -->
<auth-constraint>
<role-name>ejbRole</role-name>
<role-name>jmxRole</role-name>
</auth-constraint>
<user-data-constraint>
<description>no description</description>
<transport-guarantee>NONE</transport-guarantee>
</user-data-constraint>
</security-constraint>
<!-- Default login configuration uses form-based authentication -->
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>LdapRealm</realm-name>
</login-config>
<!-- Security roles referenced by this web application -->
<security-role><role-name>ejbRole</role-name></security-role>
<security-role><role-name>jmxRole</role-name></security-role>
</web-app> |
 |
Both the ejbRole and jmxRole are granted access to the helloWorld.jsp file. Create a file named file WEB-INF/jboss-web.xml in the current working directory and add the following contents: <?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<ejb-ref>
<ejb-ref-name>ejb/Hello</ejb-ref-name><jndi-name>osmsHello</jndi-name>
</ejb-ref>
<security-domain>java:/jaas/LdapRealm</security-domain>
</jboss-web |
The element <security-domain> should be defined in the $JBOSS_HOME/server/default/conf/login-config.xml file. Pack the files in WEB-INF and helloWorld.jsp into a WAR file by entering the following: # jar cvf ejbTest.war
WEB-INF/ helloWorld.jsp Copy the WAR package to the JBoss environment by entering
the following: # cp ejbTest.war $JBOSS_HOME/ server/default/deploy The following messages appear, indicating the Web application
deployed successfully: INFO [TomcatDeployer] deploy, ctxPath=/ejbTest, warUrl=.../tmp/deploy/tmp41792ejbTest-exp.war/ Open a browser and navigate to the website located at: http://<YOUR_JBOSS_SERVER>:8080/ejbTest/helloWorld.jsp When the login window is displayed, enter benw as the user and ben as the password. Verify that access is denied as displayed inFigure 23: The messages indicate that the user benw does not have permission to invoke helloWorld()(). Open a browser and navigate to the website located at: http://<YOUR_JBOSS_SERVER>:8080/ejbTest/helloWorld.jsp When the login window is displayed, enter tomy as the user and tom as the password. Verify
that access is granted as displayed in Figure 24 : The message indicates the user tomy has permission to invoke the method helloWorld()().
|