This exercise will step you through the setup of security within the web tier. We will re-integrate the web tier with the newly secured EJB tier and then add some layers of security on the web tier itself. It is very critical to have the EJB tier ensure security policies are being applied. The web tier also has a duty to aid in implementing the business rules of the application and extend the security protections to users integrated through the web UI.
//web page response General Exception Page An error was reported by the application. More detailed information may follow. . javax.ejb.EJBAccessException: JBAS014502: Invocation on method: public abstract java.util.Collection myorg.javaeeex.ejb.RegistrarLocal.getAllPeople(int,int) throws myorg.javaeeex.bl.RegistrarException of bean: RegistrarEJB is not allowed ...
//server log ... 10:37:30,660 DEBUG [myorg.javaeeex.web.RegistrarHandlerServlet:99] command=Get All People 10:37:30,749 ERROR [org.jboss.as.ejb3.component.interceptors.LoggingInterceptor:66] JBAS014134: EJB Invocation failed on component RegistrarEJB for method public abstract java.util.Collection myorg.javaeeex.ejb.RegistrarLocal.getAllPeople(int,int) throws myorg.javaeeex.bl.RegistrarException: javax.ejb.EJBAccessException: JBAS014502: Invocation on method: public abstract java.util.Collection myorg.javaeeex.ejb.RegistrarLocal.getAllPeople(int,int) throws myorg.javaeeex.bl.RegistrarException of bean: RegistrarEJB is not allowed ...
Notice how we were immediately rejected with no chance to login. This is an indication that our WAR is not yet configured to implement a security login. However, since some of our EJB methods are configured for anonymous callers -- we can invoke some methods successfully without effort.
$ cat javaeeExEJB/src/main/resources/META-INF/jboss-ejb3.xml
...
<assembly-descriptor>
<sec:security>
<ejb-name>*</ejb-name>
<sec:security-domain>other</sec:security-domain>
</sec:security>
</assembly-descriptor>
...$ cat
@Stateless
@RolesAllowed({"user"})
public class RegistrarEJB implements RegistrarLocal, RegistrarRemote {
...
@PermitAll
public void ping() {
log.debug("ping called");
log.debug("caller=" + ctx.getCallerPrincipal().getName());
}
...Result: ping() complete Go to Main Page
In this section we will associate the web tier with a security domain and provide the capabilty to authicate with the server.
$ cat javaeeExWAR/src/main/webapp/WEB-INF/jboss-web.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss-web PUBLIC
"-//JBoss//DTD Web Application 2.4//EN"
"http://www.jboss.org/j2ee/dtd/jboss-web_4_0.dtd">
<jboss-web>
<security-domain>other</security-domain>
</jboss-web>$ cat javaeeExWAR/src/main/webapp/WEB-INF/web.xml
...
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>javaeeEx</realm-name>
</login-config>
<security-role>
<role-name>admin</role-name>
</security-role>
</web-app>$ cat javaeeExWAR/src/main/webapp/WEB-INF/web.xml
...
<servlet-mapping>
<servlet-name>AdminHandler</servlet-name>
<url-pattern>/model/admin/handler</url-pattern>
</servlet-mapping>
...
</filter-mapping>
<security-constraint>
<web-resource-collection>
<web-resource-name>admin-access</web-resource-name>
<url-pattern>/model/admin/handler</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>NONE</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
...$ mvn clean install -rf :javaeeExWAR -DskipTests; mvn pre-integration-test -rf :javaeeExTest ... [INFO] BUILD SUCCESS
$ cat JBOSS_HOME/standalone/configuration/application-roles.properties ... admin2=admin
$ cat javaeeExWAR/src/main/webapp/WEB-INF/web.xml
...
<web-resource-collection>
<web-resource-name>admin-access</web-resource-name>
<url-pattern>/admin/*</url-pattern>
<url-pattern>/model/admin/handler</url-pattern>
</web-resource-collection>
...$ mvn clean install -rf :javaeeExWAR -DskipTests; mvn pre-integration-test -rf :javaeeExTest ... [INFO] BUILD SUCCESS
You currently have enough to implement some basic security. However, with the BASIC login-config we don't have much of a chance to interact with the user (e.g., what if they have forgotten their credentials) and it is also impossible to logout without closing the browser. We will fix this issue by implementing a FORM login-config and by adding a logout feature.
$ cat javaeeExWAR/src/main/webapp/WEB-INF/content/Login.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head><title>JavaEE Exercise Login Form</title></head>
<body>
<h1>Login Required</h1>
<form action="j_security_check" method="POST">
<table border="0" width="30%" cellspacing="3" cellpadding="2">
<tr>
<td><b>User Name</b></td>
<td><input type="text" size="20" name="j_username"></td>
</tr>
<tr>
<td><b>Password</b></td>
<td><input type="password" size="10" name="j_password"></td>
</tr>
<tr>
<td><p><input type="submit" value="Login"></td>
</tr>
</table>
</form>
</body>
</html>$ cat javaeeExWAR/src/main/webapp/WEB-INF/content/LoginFailure.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head><title>SecurePing Login Form</title></head>
<body>
<h1>Login Failure</h1>
<form action="j_security_check" method="POST">
<table border="0" width="30%" cellspacing="3" cellpadding="2">
<tr>
<td><b>User Name</b></td>
<td><input type="text" size="20" name="j_username"></td>
</tr>
<tr>
<td><b>Password</b></td>
<td><input type="password" size="10" name="j_password"></td>
</tr>
<tr>
<td><p><input type="submit" value="Login"></td>
</tr>
</table>
</form>
<p/>
Test accounts:
<ul>
<li>admin1/password1! - an admin that is only an admin</li>
<li>admin2/password1! - an admin that is also a user</li>
<li>user1/password1!</li>
<li>known/password1! - someone who has a login, but no permissions</li>
</ul>
</body>
</html>$ cat javaeeExWAR/src/main/webapp/WEB-INF/web.xml
...
<login-config>
<!--
<auth-method>BASIC</auth-method>
<realm-name>javaeeEx</realm-name>
-->
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/WEB-INF/content/Login.jsp</form-login-page>
<form-error-page>/WEB-INF/content/LoginFailure.jsp</form-error-page>
</form-login-config>
</login-config>$ mvn clean install -rf :javaeeExWAR -DskipTests; mvn pre-integration-test -rf :javaeeExTest ... [INFO] BUILD SUCCESS
$ cat javaeeExWAR/src/main/java/myorg/javaeeex/web/RegistrarHandlerServlet.java
...
private abstract class Handler {
...
protected static final String MAIN_MENU_URL =
"/index.jsp";
...
private class Logout extends Handler {
@Override
public void doHandle(HttpServletRequest request,
HttpServletResponse response) throws Exception {
request.getSession().invalidate();
response.sendRedirect(request.getContextPath() + MAIN_MENU_URL);
}
}$ cat javaeeExWAR/src/main/java/myorg/javaeeex/web/RegistrarHandlerServlet.java
...
public class RegistrarHandlerServlet extends HttpServlet {
...
public static final String LOGOUT_COMMAND = "logout";
...
public void init() throws ServletException {
...
if (ADMIN_TYPE.equals(handlerType)) {
...
}
else if (ANONYMOUS_TYPE.equals(handlerType)) {
...
}
handlers.put(LOGOUT_COMMAND, new Logout());$ cat javaeeExWAR/src/main/webapp/index.jsp
...
<li><a href="model/handler?command=logout">Logout</a></li>
...$ mvn clean install -rf :javaeeExWAR -DskipTests; mvn pre-integration-test -rf :javaeeExTest ... [INFO] BUILD SUCCESS
You implemented authentication in the previous sections by prompting the user for username and password information using one of two techniques; BASIC or FORM. Although this is functionally sufficient to authenticate the user, it presents a security problem because the credentials are passed to the server in the clear (actually they are obfuscated but still not encrypted). In this section we will add access to admin functions via HTTPS.
$ cat javaeeExWAR/src/main/webapp/WEB-INF/web.xml
...
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
...$ mvn clean install -rf :javaeeExWAR -DskipTests; mvn pre-integration-test -rf :javaeeExTest ... [INFO] BUILD SUCCESS
$ cd jboss-as-7.1.1.Final/standalone/configuration $ keytool -genkey -alias server -keyalg RSA -keystore server.keystore -storepass password -keypass password -dname "CN=127.0.0.1" -validity 365 $ ls ... server.keystore ...
$ keytool -export -alias server -keystore server.keystore -storepass password -file server.cer Certificate stored in file <server.cer> $ ls ... server.cer server.keystore ...
<subsystem xmlns="urn:jboss:domain:web:1.1" default-virtual-server="default-host" native="false">
<connector name="http" protocol="HTTP/1.1" scheme="http" socket-binding="http"/>
<virtual-server name="default-host" enable-welcome-root="true">
<alias name="localhost"/>
<alias name="example.com"/>
</virtual-server>
</subsystem> <connector name="https" protocol="HTTP/1.1" scheme="https" socket-binding="https" secure="true" enable-lookups="false">
<ssl password="password" certificate-key-file="${jboss.server.config.dir}/server.keystore"
protocol="TLSv2" verify-client="false"/>
</connector><connector name="http" protocol="HTTP/1.1" scheme="http" socket-binding="http" redirect-port="8443"/>
<subsystem xmlns="urn:jboss:domain:web:1.1" default-virtual-server="default-host" native="false">
<connector name="http" protocol="HTTP/1.1" scheme="http" socket-binding="http" redirect-port="8443"/>
<connector name="https" protocol="HTTP/1.1" scheme="https" socket-binding="https" secure="true" enable-lookups="false">
<ssl password="password" certificate-key-file="${jboss.server.config.dir}/server.keystore"
protocol="TLSv2" verify-client="false"/>
</connector>
<virtual-server name="default-host" enable-welcome-root="true">
<alias name="localhost"/>
<alias name="example.com"/>
</virtual-server>
</subsystem>$ cat javaeeExWAR/src/main/java/myorg/javaeeex/web/RegistrarHandlerServlet.java
import javax.annotation.Resource;
...
public class RegistrarHandlerServlet extends HttpServlet {
...
@Resource(name="httpPort")
Integer httpPort;
...
private class Logout extends Handler {
...
request.getSession().invalidate();
//switch back to straight HTTP
String contextPath = new StringBuilder()
.append("http://")
.append(request.getServerName())
.append(":")
.append(httpPort)
.append(request.getContextPath())
.toString();
response.sendRedirect(contextPath + MAIN_MENU_URL);
... <env-entry>
<env-entry-name>httpPort</env-entry-name>
<env-entry-type>java.lang.Integer</env-entry-type>
<env-entry-value>${jboss.servlet.port}</env-entry-value>
</env-entry> $ cat javaeeExWAR/pom.xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<webResources>
<resource>
<directory>${basedir}/src/main/webapp/WEB-INF</directory>
<filtering>true</filtering>
<targetPath>WEB-INF</targetPath>
<includes>
<include>web.xml</include>
</includes>
</resource>
</webResources>
</configuration>
</plugin>$ cat pom.xml
<properties>
...
<jboss.servlet.port>8080</jboss.servlet.port>$ cat pom.xml
<properties>
...
<maven-war-plugin.version>2.2</maven-war-plugin.version>$ cat pom.xml
<build>
<pluginManagement>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>${maven-war-plugin.version}</version>
</plugin>
$ mvn clean install -rf :javaeeExWAR -DskipTests ... ] --- maven-war-plugin:2.2:war (default-war) @ javaeeExWAR --- [INFO] Packaging webapp [INFO] Assembling webapp [javaeeExWAR] in [/home/jcstaff/proj/exercises/javaeeEx/javaeeExWAR/target/javaeeExWAR-1.0-SNAPSHOT] [INFO] Processing war project [INFO] Copying webapp webResources [/home/jcstaff/proj/exercises/javaeeEx/javaeeExWAR/src/main/webapp/WEB-INF] to [/home/jcstaff/proj/exercises/javaeeEx/javaeeExWAR/target/javaeeExWAR-1.0-SNAPSHOT] [INFO] Copying webapp resources [/home/jcstaff/proj/exercises/javaeeEx/javaeeExWAR/src/main/webapp] [INFO] Webapp assembled in [114 msecs] [INFO] Building war: /home/jcstaff/proj/exercises/javaeeEx/javaeeExWAR/target/javaeeExWAR-1.0-SNAPSHOT.war [INFO] WEB-INF/web.xml already added, skipping
$ cat javaeeExWAR/target/javaeeExWAR-1.0-SNAPSHOT/WEB-INF/web.xml
...
<env-entry>
<env-entry-name>httpPort</env-entry-name>
<env-entry-type>java.lang.Integer</env-entry-type>
<env-entry-value>8080</env-entry-value>
</env-entry>
</web-app>$ mvn clean install -rf :javaeeExWAR -DskipTests; mvn pre-integration-test -rf :javaeeExTest ... BUILD SUCCESS
In this exercise we enabled security within the web tier to not only better secure access to out application but to re-enable functionality to authorized users and better enforce the secuity policies of the application.
The following is an overview of the primary modules accessed during this exercise.
javaeeExWAR/
|-- pom.xml
`-- src
|-- main
| |-- java
| | `-- myorg
| | `-- javaeeex
| | `-- web
| | |-- JPAFilter.java
| | `-- RegistrarHandlerServlet.java
| `-- webapp
| |-- admin
| | `-- admin_menu.jsp
| |-- index.jsp
| `-- WEB-INF
| |-- beans.xml
| |-- content
| | |-- DisplayException.jsp
| | |-- DisplayPeople.jsp
| | |-- DisplayPerson.jsp
| | |-- DisplayResult.jsp
| | |-- ErrorPage.jsp
| | |-- LoginFailure.jsp
| | |-- Login.jsp
| | `-- UnknownCommand.jsp
| |-- jboss-web.xml
| `-- web.xml
`-- test
`-- resources
`-- log4j.xml