users@glassfish.java.net

Re: More control on Realm / LoginModule login

From: <glassfish_at_javadesktop.org>
Date: Tue, 18 May 2010 14:41:08 PDT

Sorry for the late reply, you probably have already found a solution for this (if so, let us know!), but here is a suggestion out of the box...

This will only work if you use form-based login, and it's not a really super-beautiful solution, but i think it's appserver independent and at least it's very simple. I just implemented it in my own app to try it within 15 minutes. :)

Assuming you are using form-based login and have a login JSP, use code along these lines to count the number of failed login attempts and take some special action when it reaches some treshold:

(login.jsp)

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>Login</title>
</head>
<body>
        <h1>Login</h1>
        
        <c:choose>
                <c:when test="${empty login_failed}">
        <p>Please enter your credentials.</p>
                </c:when>
                <c:otherwise>
                        <%
                                // At this point, login failed. Store the amount of failed attempts in the
                                // session and possibly take some action when it exceeds some threshold.
                                String LOGIN_ATTEMPTS = "failedLoginAttempts";
                                int FAILURE_THRESHOLD = 3;

                                // First get the current amount of failed attempts from the session, if any
                                int failedAttempts = 0;
                                Object attempts = request.getSession().getAttribute(LOGIN_ATTEMPTS);
                                if (attempts instanceof Integer)
                                        failedAttempts = ((Integer) attempts).intValue();
                                
                                // Now increase the number
                                failedAttempts++;
                                
                                // Store the increased number in the session
                                request.getSession().setAttribute(LOGIN_ATTEMPTS, new Integer(failedAttempts));
                                
                                if (failedAttempts > FAILURE_THRESHOLD) {
                                        // Do some smart things here, such as special logging of IP address...
                        %>
        <p class="login-failed">Too many failed logins. Your IP address was logged.</p>
                        <%
                                }
                                else {
                        %>
        <p class="login-failed">Login failed, please try again.</p>
                        <%
                                }
                        %>
                </c:otherwise>
        </c:choose>
        
        <form method="post" action="j_security_check">
                <div class="field username mandatory">
                        <label for="username">Username *</label>
                        <input id="username" name="j_username" type="text" class="text"
                                        value="${username}" />
                </div>
                <div class="field password">
                        <label for="password">Password</label>
                        <input id="password" name="j_password" type="password" class="password"
                                        value="${password}" />
                </div>
                <div class="buttons">
                        <input type="submit" id="login" class="button ok submit" value=" Login " />
                </div>
    </form>
</body>
</html>

This is my actual login.jsp, but the attempt counting I added just now for testing my solution. It's working. :)

I am actually using a servlet that uses

request.getRequestDispatcher("/WEB-INF/jsp/auth/login.jsp").forward(request, response)

to forward the request to the JSP for rendering so actually it would make a lot more sense to put the counting code in there, but the above method will also work if you are not using a servlet between it and are using JSP directly.

Just for reference, here is the login config in web.xml:

        <login-config>
                  <auth-method>FORM</auth-method>
                  <form-login-config>
                        <form-login-page>/auth/login</form-login-page>
                        <form-error-page>/auth/login?error=true</form-error-page>
                  </form-login-config>
        </login-config>

As you can see I am using the same file both for normal login as for the error page, just adding the parameter error=true in case of an error. This is the parameter I am checking for in the JSP.

Any variation of this can be dreamed up but the moral of the story is that I think it's easiest to keep the counting and logging of IP stuff out of the loginmodule and just use the results of your login module indirectly through the error page. :)
[Message sent by forum member 'stijn_de_witt']

http://forums.java.net/jive/thread.jspa?messageID=470343