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!

Jaas authentication mechanism – is it possible to force j_security_check to go to a specific page?

A proper answer to my question in the title would be – no. There are no good solutions of doing this, and even if you can come up with something that would work there are always black holes and pits to fall in that are difficult to handle. Not to mention that if you do manage to do something it should be considered a hack rather than a soluton. Still, there are things that can be done, and I will show an example here. But please have in the back of your head what I’ve just said, this is not a fool-proof solution.

The way Jaas works is that you specify some protected resources like this in your web.xml:

 <security-constraint>

  <web-resource-collection>
   <web-resource-name>PROTECTED AREA</web-resource-name>
   <description>Require users to authenticate</description>
   <url-pattern>/pages/protected/*</url-pattern>
  </web-resource-collection>

And then, if you try to send a request or get a page that is protected, JAAS brings up the login for you. This can be either BASIC (which means that a dialog box of your browser will be rendered), or FORM based, where you can specify your login pages like this:

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

[/sourceode]

The form you use to login would be something like this:


<form method="POST" name="loginform" action="j_security_check">
                        <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>

If you successfully login, JAAS will forward you to the page you initially requested. If your login fails, it will bring up the loginFailed page (if you used FORM method).This works quite well, but there can be situations where you would like the user to always go to the same page when logging in, or even select which page to go to BEFORE logging in. My example let’s you handle this, up to some point at least. As I said initially, there are no good solutions to do this.But to simplify, let’s say that we use FORM based login, and in our login page the user can select whether he/she wants to go to page A or page B. We will use radio-boxes to let the user choose between this. To give you an idea about the flow we want to follow I wil lgive you a brief overview:

  • First, when starting or logging in to our application, the url have to go to a fixed protected page, let’s call it navigation.jsp e.g. When we try to reach this page, JAAS will trigger our authentication form.
  • We don’t set the action of our login form to “j_security_check,” but to a servlet called Login.
  • When we press Login, the servlet gets the username, password and selectedPage from the request.
  • The servlet then builds up a redirectUrl, sets it in the session, but redirects to j_security_check and passes the username and password as parameters.
  • J_security_check will approve our logon, and will forward us to the initial requested page (navigation.jsp)
  • In navigation jsp, we get the session parameter, and then redirect again. The user is now at the page he/she selected.

The form we will use to log on will in other words be something like this:


		<form method="POST" name="loginform" action="/Login">
                        <table style="vertical-align: middle;">
 			<tr><td>Username:</td><td><input type="text" name="j_username"/></td></tr>
                         <tr><td>assword:</td><td><input type="password" name="j_password"/></td></tr>
                       		<td>A</td><td><input type="radio" name="targetPage" value="a" checked="checked" /> </td>
                         <td>B</td><td><input type="radio" name="targetPage" value="b" /> </td>
        	                 <tr><td><input type="submit" value="Login" /></td></tr>
 		</table>
 	</form>

We see that the radio button with value ‘a’ is checked by default, and that the action now points to our servlet instead. Our servlet would be something like this:


public class Login extends HttpServlet
{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException
    {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException
    {
        String target = req.getParameter("targetPage");
        String user = req.getParameter("j_username");
        String pass = req.getParameter("j_password");

        String redirectUrl = "";

        if(target.equalsIgnoreCase("a"))
             redirectUrl = "TheUrlWeWantToGoToForPageA";
        else if(target.equalsIgnoreCase("b"))
            redirectUrl = "TheUrlWeWantToGoToForPageB";
        else
            redirectUrl = "SomeDefaultPageJustInCaseSomethingHappens";

        req.getSession().setAttribute("redirectUrl", redirectUrl);
        resp.sendRedirect("j_security_check?j_username="+user+"&j_password="+pass);
        return;
    }
}

And our navigation page which will handle the redirection based on the user’s selection is very plain and simple:


<%
    String url = (String)request.getSession().getAttribute("redirectUrl");
    response.sendRedirect(url);
%>

If you have made it this far, you should be able to do a logon to your application, and be forwarded to the page you selected in the login form. But let’s say the login failed, you passed some wrong credentials, the way it is now it would bring up a blank page because it’s the login servlet that is “in control.” In other words, we need to do some more manual work in the login servlet before we pass things on to the security check. If the wrong username or password was entered, we should also give the user a note about this, so we will add some functionality to our login page to display potential messages returned from the login servlet.First we modify our login page to get a statusMessage from the request, and if it is not empty, we display it. Before the login-form itself we add this code:

 <%

String statusMessage= "";

statusMessage = (String)request.getSession().getAttribute("statusMessage");
request.getSession().removeAttribute("statusMessage");

boolean renderMessage = false;
if(StringUtils.isNotBlank(statusMessage))
 renderMessage = true;

%>

Then at the bottom of this login page we add some code to display the message (if it’s there):

 <%
 if(renderMessage)
 {
%>
 <table align="center" width="35%">
 <tr>
 	<td>
 		<table align="center">
 			<tr>
 				<td>
 				<%= statusMessage %>
 				</td>
 			</tr>
 		</table>
 	</td>
 </tr>
 </table>
<%
 }
%>

Then we also add some tests in our servlet to detect different “cases” that might arise. Note that to do this properly you would need some methods that could give you information from the database or the security domain you use. For simplicity I imagine I have a class DBHelper that provides me some utility methods:


public class Login extends HttpServlet
{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException
    {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException
    {
        String target = req.getParameter("targetPage");
        String user = req.getParameter("j_username");
        String pass = req.getParameter("j_password");

        String redirectUrl = "";
        String statusMessage = "";

        DBHelper dbHelper = new DBHelper();

        //Test if user exists
        if(dbHelper.doesTheUserExist(user))
        {
            //To avoid blank screen on wrong password test if password is valid
            if(dbHelper.isThePasswordCorrect(user, pass))
            {
                        if(target.equalsIgnoreCase("a"))
                            redirectUrl = "TheUrlWeWantToGoToForPageA";
                        else if(target.equalsIgnoreCase("b"))
                            redirectUrl = "TheUrlWeWantToGoToForPageB";
                        else
                            redirectUrl = "SomeDefaultPageJustInCaseSomethingHappens";

                        req.getSession().setAttribute("redirectUrl", redirectUrl);
                        resp.sendRedirect("j_security_check?j_username="+user+"&j_password="+pass);
                        return;
            }
            else
            {            statusMessage = "The login failed - combination of username/password is invalid. Please try again.";
                        redirectUrl = Configurator.getInstance().getHost()+"/"+Configurator.getInstance().getContextRoot()+"/login.faces";
            }
        }
        else
        {
            statusMessage = "The user you tried to log on with doesn't exist.";
            redirectUrl = Configurator.getInstance().getHost()+"/"+Configurator.getInstance().getContextRoot()+"/login.faces";
        }

        //If we reach this point we should go back to the login page. We also set the status message to explain to the user.
        req.getSession().setAttribute("callbackMessage", callbackMessage);
        resp.sendRedirect(redirectUrl);
    }
}

Ok, as I said in the beginning this “solution” should not be considered a proper one, passing the credentials through the parameter string e.g. is not actually a good way of doing things. But I’ll leave that for you to decide.. 😉

Automatic logon to a web application (JSF on jboss-4.2.2.GA) using JCIFS NTLM HTTP Authentication

The goal was to be able to make users which are logged in on a windows domain to automatically authenticate with their windows credentials when starting a web application, and also to make this solution possible both for IE and Firefox. In other words, the user doesn’t have to enter user-name/password when starting a corporate web application.

The web-app is done in JSF (myfaces 1.1.5), the application server is jboss-4.2.2.GA and the library I use to “grab” the windows user is JCIFS NTLM HTTP Authentication version 1.2.17 (http://jcifs.samba.org/). I will try to give a step-by-step explanation to what I did to make this work, and I will point out the differences you need to do compared to JAAS authentication mechanism (which is commonly used with applications running on jboss).

  1. The first ting you need to do is to download and put the jcifs library (jcifs-1.2.17.jar – or newer version) into your JBOSS_HOME/server/<NAME-OF-SERVER>/lib -folder.
  2. In your web.xml, you need to apply the following:
    <filter>
    
    	<filter-name>NtlmHttpFilter</filter-name>
    
    	<filter-class>jcifs.http.NtlmHttpFilter</filter-class>
    
    	<init-param>
    
    		<param-name>jcifs.http.domainController</param-name>
    
    		<param-value>IP ADDRESS_OF_THE_DOMAIN_CONTROLLER</param-value>
    
    	</init-param>
    
    	<init-param>
    
    		<param-name>jcifs.smb.client.domain</param-name>
    
    		<param-value>NAME_OF_DOMAIN</param-value>
    
    	</init-param>
    
    	<init-param>
    
    		<param-name>jcifs.smb.client.username</param-name>
    
    		<param-value>A_USERNAME_IN_ACTIVE_DIRECTORY</param-value>
    
    	</init-param>
    
    	<init-param>
    
    		<param-name>jcifs.smb.client.password</param-name>
    
    		<param-value>PASSWORD_FOR_THIS_USER</param-value>
    
    	</init-param>
    
    	<init-param>
    
    		<param-name>jcifs.util.loglevel</param-name>
    
    		<param-value>3</param-value>
    
    	</init-param>
    
    </filter>
    
    <filter-mapping>
    
    	<filter-name>NtlmHttpFilter</filter-name>
    
    	<url-pattern>/*</url-pattern>
    
    </filter-mapping>

    For the jcifs.http.domainController you specify the ip-address of the domainController you are authenticating against, e.g. 192.168.1.100.

    For jcifs.smb.client.domain you specify the domain that the users are logging in to. If you are uncertain about this check the logon-box in windows (XP login to domain) and write the domain name as its written here.

    In some examples on the net the jcifs.smb.client.username and jcifs.smb.client.password are not included. These are used for pre-authentication, think of it as a user that has access to the active directory so that it can retrieve information about the other users trying to log in. My experience is that if I leave out these two parameters, it works fine for the first user logging in, but for the first user only. When the second user logs in you get a 0xC0000022: jcifs.smb.SmbAuthException in the server log, and the negotiation of user-name/password fails. Including those parameters solves this issue. You could perhaps create a “dummy user” in the Active Directory just for this purpose (if you have administrator rights), if not use any other user of the AD.

    At least when trying to get this up and running I would recommend a logging-level set to 3. And of course you would set the url-pattern of the filter according to the path of the resources you want to protect, e.g. <url-pattern>/myJsps/*</url-pattern>

  3. If you are used to restricting access to your web application, you might have something like this in your web.xml:
    <security-constraint>
    
    	<web-resource-collection>
    
    		<web-resource-name>PROTECTED AREA</web-resource-name>
    
    		<description>Require users to authenticate</description>
    
    		<url-pattern>/*</url-pattern>
    
    	</web-resource-collection>
    
    	<auth-constraint>
    
    		<role-name>SOME_ROLE</role-name>
    
    	</auth-constraint>
    
    </security-constraint>
    
    <login-config>
    
    	<auth-method>BASIC</auth-method>
    
    	<realm-name>MY_NAME</realm-name>
    
    </login-config>
    
    <security-role>
    
    	<role-name>SOME_ROLE</role-name>
    
    </security-role>

    And then you would also have a security-domain set up in jboss-web.xml that points to an application-policy in login-config.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <!DOCTYPE jboss-web PUBLIC "-//JBoss//DTD Web Application 2.3//EN"
    
    "http://www.jboss.org/j2ee/dtd/jboss-web_3_2.dtd">
    
    <jboss-web>
    
    	<security-domain>java:/jaas/MY_APPLICATION_POLICY</security-domain> -->
    
    	<context-root>/MY_CONTEXT_ROOT</context-root>
    
    </jboss-web>

    To be able to use the security filter with success, you would need to remove all of the settings related to security and JAAS. Remove the <security-constraint> (including children) , <login-config> and <security-role> tags from web.xml, and also delete the line specifying the <security-domain> in jboss-web.xml

  4. All the settings server-side are done now, what remains is a very simple configuration of the client/web browser. You need to tell your browser that the host/site you are trying to reach should be considered a trusted site. If you don’t do this, the browser would pop up the usual login-dialog and ask for username and password.
    • Enable it in IE:Go to tools->Internet Options->SecuritySelect Local Intranet icon and press “Sites”Press “Advanced”Fill in the url to the host of your application and press add. (Do not use the full application path or port settings, only the host address – for example: http://192.168.1.100)
    • Enable it in Firefox:Type about:config in the url-barLocate the key network.automatic-ntlm-auth.trusted-urisAdd the url to the host in the value-field. If there is other urls specified, separate them with a comma.
  5. If you now try to start your application, you should be automatically logged in with your windows user. The server log should say something like this if you have a successful authentication:
    [STDOUT] NtlmHttpFilter: THE_DOMAIN\user_name successfully authenticated
    
    against 0.0.0.0<00>/IP_OF_DOMAIN_CONTROLLER

I hope this overview can be helpful, but of course there are situations and special cases that I haven’t covered here. I would have to point you to the documentation and the FAQ on the homepage of the JCIFS library (see link in the start of this post). Still, I spent some time sorting out configurations, parameters and mistakes, so hopefully this will give others a shorter path to the goal.

Good luck!