Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement self-managing web users with Java EE 6?

I want to create a simple web application that will have self-registering users. In other words, I want users to be able to register, log on, manage their data, log off, and if they want, delete their account. It is kind of a hobby project and just to learn something new, I decided to try to create this web site using latest Java EE, which I have not used before.

I spent several hours reading about JAAS, authentication realms, etc and I found lots of ways to implement security based on users and roles defined on the server (like GlassFish), but I have not found any examples or pointers to how to implement a solution where users can just fill out a registration form on a web page and become users in the system. Of course, I can simple have a database table with usernames and password hashes and implement everything manually, but it defeats the purpose.

Therefore, my questions are:

  1. Is Java EE's standard authentication and security framework suitable for implementing an app where users can manage themselves? Or is it more "enterprise" and relies on external authentication and user management?
  2. If it is easy to do what I need, please point me to some samples.

Thanks!

like image 331
Alex Avatar asked Apr 08 '11 04:04

Alex


1 Answers

The J2EE is not very good at this. I have done that in an application though. I have a table of users in the (only) database used by my application, and I have declared a realm in the context.xml file in the META-INF directory of the webapp (I'm using tomcat btw):

<Realm className="org.apache.catalina.realm.DataSourceRealm"
        dataSourceName="jdbc/fantastico" debug="99" localDataSource="true"
        digest="MD5" roleNameCol="ROLE" userCredCol="PASSWORD"
        userNameCol="USERNAME" userRoleTable="user_roles"
        userTable="active_users"/>

the datasource is the same as the one used in the rest of the application and is declared in the server.xml of the tomcat installation (so that I can point to another db without changing the .war file).

I have protected locations with roles in the web.xml file, making sure I have left the registration page and the login page uprotected:

<security-constraint>
    <display-name>Administrators</display-name>
    <web-resource-collection>
        <web-resource-name>admin</web-resource-name>
        <description>Admin</description>
        <url-pattern>/admin</url-pattern>
        <http-method>GET</http-method>
        <http-method>POST</http-method>
        <http-method>HEAD</http-method>
        <http-method>PUT</http-method>
        <http-method>OPTIONS</http-method>
        <http-method>TRACE</http-method>
        <http-method>DELETE</http-method>
    </web-resource-collection>
    <auth-constraint>
        <description>Allow access only to administrators</description>
        <role-name>admin</role-name>
    </auth-constraint>
</security-constraint>

I have specified a <login-config> section with FORM as the auth method:

<login-config>
    <auth-method>FORM</auth-method>
    <realm-name/>
    <form-login-config>
        <form-login-page>/login/</form-login-page>
        <form-error-page>/login/error</form-error-page>
    </form-login-config>
</login-config>

Since the realm is declared as using an MD5 digest, it's the MD5 of the password that must be store in the database:

private static final char HEX[] = "0123456789abcdef".toCharArray();


public static String hex(byte data[]) {
    char[] chars = new char[2*data.length];
    for (int i=0; i < data.length; i++) {
        int low = data[i] & 0x0f;
        int high = (data[i] & 0xf0) >> 4;
        chars[i*2] = HEX[high];
        chars[i*2 + 1] = HEX[low];
    }
    return new String(chars);
}

public static byte[] hash(byte data[]) {
    synchronized (DIGEST) {
        DIGEST.reset();
        return DIGEST.digest(data);
    }
}

public static String hash(String value) {
    try {
        byte data[] = hash(value.getBytes("UTF-8"));
        return hex(data);
    } catch (UnsupportedEncodingException e) {
        // Cannot happen: UTF-8 support is required by Java spec
        LOG.error("Failed to generate digest", e);
        throw new RuntimeException(e.getMessage());
    }
}

In the login form, that tomcat will display whenever an authentication is required, I have added a link to the registration form:

...

<form id="login-form" action="j_security_check" method="post">
    <table>
        <tbody>
            <tr>
                <td><label for="username">User name:&nbsp;</label></td>
                <td><input type="text" name="j_username" id="username" /></td>
            </tr>
            <tr>
                <td><label for="password">Password:&nbsp;</label></td>
                <td><input type="password" name="j_password" id="password" /></td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td><input type="submit" id="submit" name="submit" value="Login"></td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td><a href="../reg/create">I'm a new user</a></td>
            </tr>
        </tbody>
    </table>
</form>

...

Now, when the registration form is submitted, I store the user in the database, and I send a redirect to automatically log the user in:

...

response.sendRedirect(request.getContextPath()
        + "/j_security_check?j_username="
        + URLEncoder.encode(getEmail(), "UTF-8")
        + "&j_password="
        + URLEncoder.encode(password, "UTF-8"));

...

Hope this will help

like image 193
Maurice Perry Avatar answered Nov 16 '22 04:11

Maurice Perry