Using WebAuthentication in Jboss

Hi!

Because of a respone to my blog post Perform programmatic logon I decided to provide a bit more complete example of how to use the jboss WebAuthentication. I will show how to do the logon in two ways, by an action in a handler and
by doing it in a servler. Logging in by executing an action in a handler could be nice in cases where you have a page that is open to the public, but if you log in you get access
to more information or features.

First I provide an example of a login-config that I’ve tested and used successfully with the WebAuthentication method,

<application-policy name = "myApplication">
      <authentication>
      <login-module code = "org.jboss.security.auth.spi.DatabaseServerLoginModule" flag = "required">
        <module-option name = "dsJndiName">java:/myDatasource-ds</module-option>
        <module-option name = "principalsQuery">Select password from user_table where user_name =?</module-option>
        <module-option name = "rolesQuery">Select 'user', 'Roles', 'RoleGroups' from user_table where user_name =?</module-option>
        <module-option name ="hashAlgorithm">md5</module-option>
        <module-option name="hashEncoding">hex</module-option>
        <module-option name="debug">false</module-option>
     </login-module>
     <login-module code="org.jboss.security.ClientLoginModule" flag="required" />
     </authentication>
 </application-policy>

Create a LoginHandler or something similar, with a method login() and two helper-methods getRemoteUser and getUserPrincipal. Something like this:

public class LoginHandler
{

public String login()
{
        WebAuthentication webAuthentication = new WebAuthentication();

        String hardcodedUserName = "user@somewhere.com";
        String hardcodedPassword = "user123";

        if (webAuthentication.login(hardcodedUserName, hardcodedPassword))
        {
           System.out.println("Logged in successfully");;

            log.debug("userPrincipal: " + getUserPrincipal());
            log.debug("remoteUser: " + getRemoteUser());
        }
        else
        {
            log.debug("Login failed");
        }

        return "";
}
public String getUserPrincipal()
{
        FacesContext context = FacesContext.getCurrentInstance();
        ExternalContext externalContext = context.getExternalContext();
        return externalContext.getUserPrincipal() != null ? externalContext.getUserPrincipal().toString() : "null";
}

public String getRemoteUser()
{
        FacesContext context = FacesContext.getCurrentInstance();
        ExternalContext externalContext = context.getExternalContext();
        String remoteUser = externalContext.getRemoteUser();
        return remoteUser;
}

}

For this example we basically hardcode the username/password, and just test if we get login to work. You should of course use a page and navigation-rules or smth similar to set the username/password as properties in the loginHandler.
Then create a simple jsp, but don’t put it inside your protected folder. Create it one level up so that you can access it without being asked to log in.

In this jsp you will have a stupid commandButton, as well as two outputs that displays the remoteUser and userPrincipal:

<f:view >
    <t:document>
    <t:documentHead>
       <f:loadBundle basename="MessageResources" var="messages" />
    </t:documentHead>

    <f:verbatim>
        <body>
    </f:verbatim>
    <h:form>
        <h:panelGrid>
            <h:commandButton value="Login" action="#{loginHandler.login}"></h:commandButton>
            <h:outputText value="User Principal: #{loginHandler.userPrincipal}"></h:outputText>
            <h:outputText value="Remote User: #{loginHandler.remoteUser}"></h:outputText>
        </h:panelGrid>
    </h:form>
    <f:verbatim>
        </body>
    </f:verbatim>
    </t:document>
</f:view>

Now, launch your application by going to this page. You will see that userPrincipal shows “null” and remoteUser is empty. Press the Login button.
When the page reloads it should display your username in both outputTexts. If you check your log or console it should say “Logged in successfully.”
The user is now properly set in the web container. Also, if you now try to change the url to go to one of your protected pages,
you should not be redirected to any login.jsp, but the page should display properly – because you are already logged on.

Then, to do the logon from a servlet there’s really not a big difference. I set up my web.xml to use a FORM-based logon, like this:

<login-config>
    <auth-method>FORM</auth-method>
    <form-login-config>
     <form-login-page>/login.faces</form-login-page>
     <form-error-page>/loginFailed.faces</form-error-page>
    </form-login-config>
   </login-config>

I also register my LoginServlet in web.xml like this:

<servlet>
    <servlet-name>LoginServlet</servlet-name>
    <servlet-class>com.test.servlet.LoginServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>LoginServlet</servlet-name>
    <url-pattern>/LoginServlet</url-pattern>
  </servlet-mapping>

My login.jsp is 99% the same as when using j_security_check, we just point the action to the LoginServlet instead:

<form method="POST" name="loginform" action="${pageContext.request.contextPath}/LoginServlet">
<table style="vertical-align: middle;">
<tr>
<td>Username:</td>
<td><input type="text" name="j_username"/></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="password" name="j_password"/></td>
</tr>
<tr>
<td><input type="submit" value="Login" /></td>
</tr>
</table>
</form>

My LoginServlet uses the parameters that was originally passed to the j_security_check. Also, if we successfully log on, we get the “Referer” from the request to redirect to the page that originally was the target for the user. I did not include the code that handles if the logon failed here:

public class LoginServlet extends HttpServlet
{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException
    {
        // TODO Auto-generated method stub
        doPost(req, resp);

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException
    {
        String user = req.getParameter("j_username");
        String pass = req.getParameter("j_password");
        WebAuthentication webAuthentication = new WebAuthentication();
        if(    webAuthentication.login(user, pass))
        {
            String redirectUrl = "";
            String referer = req.getHeader("Referer");
            resp.sendRedirect(redirectUrl);
            return;
        }
        else
        {
            System.out.println("Login attempt failed " + user);
            ...
            Handle incorrect logon, go back to login page etc
            ...
        }
    }
}

So, to test this, start your application by pointing directly to some page that is protected. Your login.jsp should be presented, and when you press Login
the servlet uses the credentials you passed in the forms, authenticates and redirect you to the page.
For debug reasons you could add the same outputTexts here on this page (remoteUser and userPrincipal), and they SHOULD show the user you logged in with now.
There is also no problem to navigate to other protected pages.

Hope that these examples might help anybody that struggles with this!

JBoss guide: How to enable SSL (HTTPS) on JBoss, as well as other “nice-to-know” configurations

From time to time you might have the need of running a web application over https, or there can be requests of having Single-Sign-On between multiple web applications running on your server. This small jboss-guide will give you some clues on how to solve tasks like this, with configurations for both jboss-4.0.4.GA and jboss-4.2.2.GA. Since the name of the server instance might differ and it’s also possible to use custom names, I’ll refer to it as jboss/server/<NAME>/, but what I mean here is for example jboss/server/default/.

Changing the port that jboss runs on

For 4.0.4 you should locate the server.xml inside jboss/server/<NAME>/deploy/jbossweb-tomcat55.sar/, and then change the port=”8080″ parameter in the HTTP Connector to your wishes, for example port 80 as I have done it here.

 <!-- A HTTP/1.1 Connector on port 8080 -->
      <Connector port="80" address="${jboss.bind.address}"
         maxThreads="250" strategy="ms" maxHttpHeaderSize="8192"
         emptySessionPath="true"
         enableLookups="false" redirectPort="8443" acceptCount="100"
         connectionTimeout="20000" disableUploadTimeout="true"/>

For 4.2.2 you do exactly the same, but the server.xml is located inside jboss/server/<NAME>/deploy/jboss-web.deployer/ instead.

Make tomcat able to compile java5 – by default it doesn’t

If you have the need of using java5 (jdk 1.5), you need to set the source-level of the compiler. If you don’t do this and have deployed web-applications with java5 code, you will get exceptions during startup. For 4.0.4 edit the web.xml in jboss/server/<NAME>/deploy/jbossweb-tomcat55.sar/conf. Locate the jsp servlet by searching for <servlet-name>jsp</servlet-name>, and uncomment the section that enables jdk1.5 features:

  <!-- Uncomment to use jdk1.5 features in jsp pages -->
      <init-param>
         <param-name>compilerSourceVM</param-name>
         <param-value>1.5</param-value>
      </init-param>

For 4.2.2 you find the web.xml inside jboss/server/<NAME>/deploy/jbossweb-deployer/conf. Locate the same servlet, and make sure that the parameters both for source & target compiler are set like this:

   	<servlet>
        <servlet-name>jsp</servlet-name>
        <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
        <init-param>
            <param-name>fork</param-name>
            <param-value>false</param-value>
        </init-param>
        <init-param>
            <param-name>xpoweredBy</param-name>
            <param-value>false</param-value>
        </init-param>
        <init-param>
         <param-name>compilerSourceVM</param-name>
         <param-value>1.5</param-value>
        </init-param>
  	<init-param>
         <param-name>compilerTargetVM</param-name>
         <param-value>1.5</param-value>
        </init-param>

Activate support for Single Sign-On

For both versions, locate server.xml (4.0.4 = jboss/server/<NAME>/deploy/jbossweb-tomcat55.sar/, 4.2.2 = jboss/server/<NAME>/deploy/jboss-web.deployer/). Find the “Host” section, and uncomment the following Valve:

   	 <!-- Uncomment to enable single sign-on across web apps
                deployed to this host. Does not provide SSO across a cluster.     

                If this valve is used, do not use the JBoss ClusteredSingleSignOn
                valve shown below. 

                A new configuration attribute is available beginning with
                release 4.0.4:

                cookieDomain  configures the domain to which the SSO cookie
                              will be scoped (i.e. the set of hosts to
                              which the cookie will be presented).  By default
                              the cookie is scoped to "/", meaning the host
                              that presented it.  Set cookieDomain to a
                              wider domain (e.g. "xyz.com") to allow an SSO
                              to span more than one hostname.
             -->

            <Valve className="org.apache.catalina.authenticator.SingleSignOn" />

Then in your jboss-web.xml it’s important that all the web applications that are going to “exchange” credentials points to the same security-domain:

<jboss-web>
 	<security-domain>java:/jaas/USE_THE_SAME_APPLICATION_POLICY_HERE</security-domain>
	<context-root>/YOUR_APPLICATION_ROOT</context-root>
</jboss-web>

If you now open and logon to one application, going to another one running on the same server should not prompt you for username/password again. Note that there are alternatives also if you have applications running on different servers/locations – check the other Valves.

Enable SSL on JBoss

In this example I’m only using a self-signed certificate, but the procedure would be more or less the same even if you are going to use a certificate from a Certification Authority.

  1. Generate the keystore with the following command
    keytool -genkey -alias tomcat -keyalg RSA -keystore NAME_OF_KEYSTORE -validity NUMBER_OF_DAYS
  2. Copy the file into the jboss/server/<NAME>/conf/ directory
  3. Edit the server.xml (4.0.4 = jboss/server/<NAME>/deploy/jbossweb-tomcat55.sar/, 4.2.2 = jboss/server/<NAME>/deploy/jboss-web.deployer/).For 4.0.4 the SSL-connector should be configured like:
     <!-- SSL/TLS Connector configuration using the admin devl guide keystore     -->
          <Connector port="THE_PORT_YOU_LIKE" address="${jboss.bind.address}"
               maxThreads="100" strategy="ms" maxHttpHeaderSize="8192"
               emptySessionPath="true"
               scheme="https" secure="true" clientAuth="false"
               keystoreFile="${jboss.server.home.dir}/conf/THE_KEYSTORE_NAME"
               keystorePass="PASSWORD_FOR_THE_KEYSTORE" sslProtocol = "TLS" />

    For 4.2.2, configure it like this:

     <Connector port="THE_PORT_YOU_LIKE" protocol="HTTP/1.1" SSLEnabled="true"
                   maxThreads="150" scheme="https" secure="true"
                   clientAuth="false"
    	       strategy="ms"
                   address="${jboss.bind.address}"
                   keystoreFile="${jboss.server.home.dir}/conf/THE_KEYSTORE_NAME"
                   keystorePass="PASSWORD_FOR_THE_KEYSTORE"
                   truststoreFile="${jboss.server.home.dir}/conf/THE_KEYSTORE_NAME"
                   truststorePass="PASSWORD_FOR_THE_KEYSTORE"
                   sslProtocol="TLS"/>
  4. Now you should be able to access your application through https. Remember to use https:// instead of http:// in your browser-url, or else it will fail.
  5. Remember that if you want to disable the non-secured port 8080 (or custom), making sure that people can only access through https, comment and disable that connector in the same server.xml.

Tell jboss 4.2.2 to not use the bundled JSF 1.2 implementation

By default this version of jboss comes bundled with the Glassfish JSF 1.2 implementation. If you deploy web applications that use other implementations, like myfaces, you should tell jboss to use the implementations(s) deployed together with the web applications instead. Do this by adding the following to the web.xml of your application(s):

  <context-param>
     <param-name>org.jboss.jbossfaces.WAR_BUNDLES_JSF_IMPL</param-name>
     <param-value>true</param-value>
  </context-param>

Access jboss-4.2.2GA using ip address instead of localhost – use the “-b” parameter

I’ve been using the 4.0.4 version for some time, and I could start it on my machine (accessing it through localhost:8080), and access it from other machines in my network it by using the ip-address of my machine instead of localhost. With 4.2.2, you can start it the same way and it will work from your machine by going localhost. But trying to start the application remotely from other machines by using the ip-address would fail, giving you a 404.

This is because before 4.2.2.GA, jboss was always bound to the any address “0.0.0.0”. But this was considered a security issue, and this default behavior was removed. It’s now up to the user to explicitly configure this.

What you need to do to solve it is to start the jboss with another parameter, you need to set the bind address for the jboss services. The following command would start a jboss server named “myserver” on ip 192.168.100.100:

  run.bat -c myserver -b 192.168.100.100

If you now try to start the application from other machines by using this IP it works! If you use the server named default you can leave out the -c parameter. It’s also possible to revert back to the “old configuration” by using -b 0.0.0.0, but this is not recommended.Ok, I hope that this small guide might be of help to someone! 🙂