JSEC-123 - added intermediary abstract class to consolidate common executeLogin behavior

git-svn-id: https://svn.apache.org/repos/asf/incubator/jsecurity/trunk@711064 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/org/jsecurity/authc/UsernamePasswordToken.java b/src/org/jsecurity/authc/UsernamePasswordToken.java
index 6bb8341..17f52a5 100644
--- a/src/org/jsecurity/authc/UsernamePasswordToken.java
+++ b/src/org/jsecurity/authc/UsernamePasswordToken.java
@@ -98,7 +98,7 @@
      * @param password the password string submitted for authentication
      */
     public UsernamePasswordToken(final String username, final String password) {
-        this(username, password.toCharArray(), false, null);
+        this(username, password != null ? password.toCharArray() : null, false, null);
     }
 
     /**
@@ -128,7 +128,7 @@
      * @since 0.2
      */
     public UsernamePasswordToken(final String username, final String password, final InetAddress inetAddress) {
-        this(username, password.toCharArray(), false, inetAddress);
+        this(username, password != null ? password.toCharArray() : null, false, inetAddress);
     }
 
     /**
@@ -158,7 +158,7 @@
      * @since 0.9
      */
     public UsernamePasswordToken(final String username, final String password, final boolean rememberMe) {
-        this(username, password.toCharArray(), rememberMe, null);
+        this(username, password != null ? password.toCharArray() : null, rememberMe, null);
     }
 
     /**
@@ -197,7 +197,7 @@
      */
     public UsernamePasswordToken(final String username, final String password,
                                  final boolean rememberMe, final InetAddress inetAddress) {
-        this(username, password.toCharArray(), rememberMe, inetAddress);
+        this(username, password != null ? password.toCharArray() : null, rememberMe, inetAddress);
     }
 
     /*--------------------------------------------
diff --git a/src/org/jsecurity/web/WebUtils.java b/src/org/jsecurity/web/WebUtils.java
index 790d43d..1962a6c 100644
--- a/src/org/jsecurity/web/WebUtils.java
+++ b/src/org/jsecurity/web/WebUtils.java
@@ -20,6 +20,9 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jsecurity.SecurityUtils;
+import org.jsecurity.session.Session;
+import org.jsecurity.subject.Subject;
 import org.jsecurity.util.StringUtils;
 import org.jsecurity.util.ThreadContext;
 
@@ -27,7 +30,6 @@
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.net.InetAddress;
@@ -228,7 +230,7 @@
      *
      * @param request the incoming ServletRequest
      * @return the <code>InetAddress</code> associated with the current request, or <code>null</code> if the
-     * address cannot be resolved/determined.
+     *         address cannot be resolved/determined.
      */
     public static InetAddress getInetAddress(ServletRequest request) {
         InetAddress clientAddress = null;
@@ -491,9 +493,9 @@
     }
 
     public static void saveRequest(ServletRequest request) {
+        Subject subject = SecurityUtils.getSubject();
+        Session session = subject.getSession();
         HttpServletRequest httpRequest = toHttp(request);
-        HttpSession session = httpRequest.getSession();
-
         SavedRequest savedRequest = new SavedRequest(httpRequest);
         session.setAttribute(SAVED_REQUEST_KEY, savedRequest);
     }
@@ -501,7 +503,8 @@
     public static SavedRequest getAndClearSavedRequest(ServletRequest request) {
         SavedRequest savedRequest = getSavedRequest(request);
         if (savedRequest != null) {
-            HttpSession session = WebUtils.toHttp(request).getSession();
+            Subject subject = SecurityUtils.getSubject();
+            Session session = subject.getSession();
             session.removeAttribute(SAVED_REQUEST_KEY);
         }
         return savedRequest;
@@ -509,12 +512,11 @@
 
     public static SavedRequest getSavedRequest(ServletRequest request) {
         SavedRequest savedRequest = null;
-
-        HttpSession session = WebUtils.toHttp(request).getSession(false);
+        Subject subject = SecurityUtils.getSubject();
+        Session session = subject.getSession(false);
         if (session != null) {
             savedRequest = (SavedRequest) session.getAttribute(SAVED_REQUEST_KEY);
         }
-
         return savedRequest;
     }
 
diff --git a/src/org/jsecurity/web/filter/AccessControlFilter.java b/src/org/jsecurity/web/filter/AccessControlFilter.java
index 9b28de3..225ce1d 100644
--- a/src/org/jsecurity/web/filter/AccessControlFilter.java
+++ b/src/org/jsecurity/web/filter/AccessControlFilter.java
@@ -44,13 +44,19 @@
      */
     public static final String DEFAULT_LOGIN_URL = "/login.jsp";
 
-    /** Constant representing the HTTP 'GET' request method, equal to <code>GET</code>. */
+    /**
+     * Constant representing the HTTP 'GET' request method, equal to <code>GET</code>.
+     */
     public static final String GET_METHOD = "GET";
 
-    /** Constant representing the HTTP 'POST' request method, equal to <code>POST</code>. */
+    /**
+     * Constant representing the HTTP 'POST' request method, equal to <code>POST</code>.
+     */
     public static final String POST_METHOD = "POST";
 
-    /** The login url to used to authenticate a user, used when redirecting users if authentication is required. */
+    /**
+     * The login url to used to authenticate a user, used when redirecting users if authentication is required.
+     */
     private String loginUrl = DEFAULT_LOGIN_URL;
 
     /**
@@ -136,11 +142,7 @@
      */
     public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
         //mapped value is ignored - not needed for most (if not all) authc Filters.
-        if (isAccessAllowed(request, response, mappedValue)) {
-            return true;
-        } else {
-            return onAccessDenied(request, response);
-        }
+        return isAccessAllowed(request, response, mappedValue) || onAccessDenied(request, response);
     }
 
     /**
diff --git a/src/org/jsecurity/web/filter/authc/AuthenticatingFilter.java b/src/org/jsecurity/web/filter/authc/AuthenticatingFilter.java
new file mode 100644
index 0000000..9c5f9cd
--- /dev/null
+++ b/src/org/jsecurity/web/filter/authc/AuthenticatingFilter.java
@@ -0,0 +1,109 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the

+ * "License"); you may not use this file except in compliance

+ * with the License.  You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing,

+ * software distributed under the License is distributed on an

+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

+ * KIND, either express or implied.  See the License for the

+ * specific language governing permissions and limitations

+ * under the License.

+ */

+package org.jsecurity.web.filter.authc;

+

+import org.jsecurity.authc.AuthenticationException;

+import org.jsecurity.authc.AuthenticationToken;

+import org.jsecurity.authc.UsernamePasswordToken;

+import org.jsecurity.subject.Subject;

+import org.jsecurity.web.WebUtils;

+

+import javax.servlet.ServletRequest;

+import javax.servlet.ServletResponse;

+import java.net.InetAddress;

+

+/**

+ * An <code>AuthenticationFilter</code> that is capable of automatically performing an authentication attempt

+ * based on the incoming request.

+ *

+ * @author Les Hazlewood

+ * @since 0.9

+ */

+public abstract class AuthenticatingFilter extends AuthenticationFilter {

+

+    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {

+        AuthenticationToken token = createToken(request, response);

+        if (token == null) {

+            String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken " +

+                    "must be created in order to execute a login attempt.";

+            throw new IllegalStateException(msg);

+        }

+        try {

+            Subject subject = getSubject(request, response);

+            subject.login(token);

+            return onLoginSuccess(token, subject, request, response);

+        } catch (AuthenticationException e) {

+            return onLoginFailure(token, e, request, response);

+        }

+    }

+

+    protected abstract AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception;

+

+    protected AuthenticationToken createToken(String username, String password,

+                                              ServletRequest request, ServletResponse response) {

+        boolean rememberMe = isRememberMe(request);

+        InetAddress inet = getInetAddress(request);

+        return createToken(username, password, rememberMe, inet);

+    }

+

+    protected AuthenticationToken createToken(String username, String password,

+                                              boolean rememberMe, InetAddress inet) {

+        return new UsernamePasswordToken(username, password, rememberMe, inet);

+    }

+

+    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject,

+                                     ServletRequest request, ServletResponse response) throws Exception {

+        return true;

+    }

+

+    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e,

+                                     ServletRequest request, ServletResponse response) {

+        return false;

+    }

+

+    /**

+     * Returns the InetAddress associated with the current subject.  This method is primarily provided for use

+     * during construction of an <code>AuthenticationToken</code>.

+     * <p/>

+     * The default implementation merely returns

+     * {@link WebUtils#getInetAddress(javax.servlet.ServletRequest) WebUtils.getInetAddress(request)}.

+     *

+     * @param request the incoming ServletRequest

+     * @return the <code>InetAddress</code> to associate with the login attempt.

+     */

+    protected InetAddress getInetAddress(ServletRequest request) {

+        return WebUtils.getInetAddress(request);

+    }

+

+    /**

+     * Returns <code>true</code> if &quot;rememberMe&quot; should be enabled for the login attempt associated with the

+     * current <code>request</code>, <code>false</code> otherwise.

+     * <p/>

+     * This implementation always returns <code>false</code> and is provided as a template hook to subclasses that

+     * support <code>rememberMe</code> logins and wish to determine <code>rememberMe</code> in a custom mannner

+     * based on the current <code>request</code>.

+     *

+     * @param request the incoming ServletRequest

+     * @return <code>true</code> if &quot;rememberMe&quot; should be enabled for the login attempt associated with the

+     *         current <code>request</code>, <code>false</code> otherwise.

+     */

+    protected boolean isRememberMe(ServletRequest request) {

+        return false;

+    }

+}

diff --git a/src/org/jsecurity/web/filter/authc/AuthenticationFilter.java b/src/org/jsecurity/web/filter/authc/AuthenticationFilter.java
index 8c05285..a9abe68 100644
--- a/src/org/jsecurity/web/filter/authc/AuthenticationFilter.java
+++ b/src/org/jsecurity/web/filter/authc/AuthenticationFilter.java
@@ -85,8 +85,8 @@
         }
 
         if (successUrl == null) {
-            throw new IllegalArgumentException("Success URL not available via saved request or by calling getSuccessUrl().  " +
-                    "One of these must be non-null for issueSuccessRedirect() to work.");
+            throw new IllegalStateException("Success URL not available via saved request or by calling " +
+                    "getSuccessUrl().  One of these must be non-null for issueSuccessRedirect() to work.");
         }
 
         WebUtils.issueRedirect(request, response, successUrl);
diff --git a/src/org/jsecurity/web/filter/authc/BasicHttpAuthenticationFilter.java b/src/org/jsecurity/web/filter/authc/BasicHttpAuthenticationFilter.java
index 426ece3..629cb49 100644
--- a/src/org/jsecurity/web/filter/authc/BasicHttpAuthenticationFilter.java
+++ b/src/org/jsecurity/web/filter/authc/BasicHttpAuthenticationFilter.java
@@ -20,19 +20,14 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.jsecurity.authc.AuthenticationException;
 import org.jsecurity.authc.AuthenticationToken;
-import org.jsecurity.authc.UsernamePasswordToken;
 import org.jsecurity.codec.Base64;
-import org.jsecurity.subject.Subject;
 import org.jsecurity.web.WebUtils;
-import static org.jsecurity.web.WebUtils.toHttp;
 
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import java.net.InetAddress;
 
 /**
  * Requires the requesting user to be {@link org.jsecurity.subject.Subject#isAuthenticated() authenticated} for the
@@ -66,15 +61,21 @@
  * @see <a href="http://en.wikipedia.org/wiki/Basic_access_authentication">Basic Access Authentication</a>
  * @since 0.9
  */
-public class BasicHttpAuthenticationFilter extends AuthenticationFilter {
+public class BasicHttpAuthenticationFilter extends AuthenticatingFilter {
 
-    /** This class's private logger. */
-    private static final Log log = LogFactory.getLog(BasicHttpAuthenticationFilter.class);    
+    /**
+     * This class's private logger.
+     */
+    private static final Log log = LogFactory.getLog(BasicHttpAuthenticationFilter.class);
 
-    /** HTTP Authorization header, equal to <code>Authorization</code> */
+    /**
+     * HTTP Authorization header, equal to <code>Authorization</code>
+     */
     protected static final String AUTHORIZATION_HEADER = "Authorization";
-    
-    /** HTTP Authentication header, equal to <code>WWW-Authenticate</code> */
+
+    /**
+     * HTTP Authentication header, equal to <code>WWW-Authenticate</code>
+     */
     protected static final String AUTHENTICATE_HEADER = "WWW-Authenticate";
 
     /**
@@ -83,10 +84,14 @@
      */
     private String applicationName = "application";
 
-    /** The authcScheme to look for in the <code>Authorization</code> header, defaults to <code>BASIC</code> */
+    /**
+     * The authcScheme to look for in the <code>Authorization</code> header, defaults to <code>BASIC</code>
+     */
     private String authcScheme = HttpServletRequest.BASIC_AUTH;
-    
-    /** The authzScheme value to look for in the <code>Authorization</code> header, defaults to <code>BASIC</code> */
+
+    /**
+     * The authzScheme value to look for in the <code>Authorization</code> header, defaults to <code>BASIC</code>
+     */
     private String authzScheme = HttpServletRequest.BASIC_AUTH;
 
     /**
@@ -144,7 +149,7 @@
      * Unless overridden by this method, the default value is <code>BASIC</code>
      *
      * @param authzScheme the HTTP <code>Authorization</code> header value that this filter will respond to as
-     * indicating a login request.
+     *                    indicating a login request.
      */
     public void setAuthzScheme(String authzScheme) {
         this.authzScheme = authzScheme;
@@ -155,7 +160,7 @@
      * the HTTP Basic challenge response.  The default value is <code>BASIC</code>.
      *
      * @return the HTTP <code>WWW-Authenticate</code> header scheme that this filter will use when sending the HTTP
-     * Basic challenge response.
+     *         Basic challenge response.
      * @see #sendChallenge
      */
     public String getAuthcScheme() {
@@ -167,7 +172,7 @@
      * HTTP Basic challenge response.  The default value is <code>BASIC</code>.
      *
      * @param authcScheme the HTTP <code>WWW-Authenticate</code> header scheme that this filter will use when
-     * sending the Http Basic challenge response.
+     *                    sending the Http Basic challenge response.
      * @see #sendChallenge
      */
     public void setAuthcScheme(String authcScheme) {
@@ -181,7 +186,7 @@
      * @param response outgoing ServletResponse
      * @return true if the request should be processed; false if the request should not continue to be processed
      */
-    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) {
+    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
         boolean loggedIn = false; //false by default or we wouldn't be in this method
         if (isLoginAttempt(request, response)) {
             loggedIn = executeLogin(request, response);
@@ -220,8 +225,8 @@
      * @param request the incoming <code>ServletRequest</code>
      * @return the <code>Authorization</code> header's value.
      */
-    protected String getAuthzHeader( ServletRequest request ) {
-        HttpServletRequest httpRequest = toHttp(request);
+    protected String getAuthzHeader(ServletRequest request) {
+        HttpServletRequest httpRequest = WebUtils.toHttp(request);
         return httpRequest.getHeader(AUTHORIZATION_HEADER);
     }
 
@@ -261,7 +266,7 @@
         if (log.isDebugEnabled()) {
             log.debug("Authentication required: sending 401 Authentication challenge response.");
         }
-        HttpServletResponse httpResponse = toHttp(response);
+        HttpServletResponse httpResponse = WebUtils.toHttp(response);
         httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
         String authcHeader = getAuthcScheme() + " realm=\"" + getApplicationName() + "\"";
         httpResponse.setHeader(AUTHENTICATE_HEADER, authcHeader);
@@ -269,31 +274,25 @@
     }
 
     /**
-     * Executes a login attempt with the provided credentials in the http header and returns <code>true</code>
-     * if the login attempt is successful and <code>false</code> otherwise.
+     * Creates an AuthenticationToken for use during login attempt with the provided credentials in the http header.
      * <p/>
      * This implementation:
      * <ol><li>acquires the username and password based on the request's
      * {@link #getAuthzHeader(javax.servlet.ServletRequest) authorization header} via the
      * {@link #getPrincipalsAndCredentials(String, javax.servlet.ServletRequest) getPrincipalsAndCredentials} method</li>
      * <li>The return value of that method is converted to an <code>AuthenticationToken</code> via the
-     * {@link #createToken(String, String, javax.servlet.ServletRequest) createToken} method</li>
-     * <li>Finally, the login attempt is executed using that token by calling
-     * {@link #executeLogin(org.jsecurity.authc.AuthenticationToken, javax.servlet.ServletRequest, javax.servlet.ServletResponse)}</li>
+     * {@link #createToken(String, String, javax.servlet.ServletRequest, javax.servlet.ServletResponse) createToken} method</li>
+     * <li>The created <code>AuthenticationToken</code> is returned.</li>
      * </ol>
      *
      * @param request  incoming ServletRequest
      * @param response outgoing ServletResponse
-     * @return true if the subject was successfully logged in, false otherwise
+     * @return the AuthenticationToken used to execute the login attempt
      */
-    protected boolean executeLogin(ServletRequest request, ServletResponse response) {
-        if (log.isDebugEnabled()) {
-            log.debug("Attempting to authenticate Subject based on Http BASIC Authentication request...");
-        }
-
+    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
         String authorizationHeader = getAuthzHeader(request);
-        if (authorizationHeader == null || authorizationHeader.length() == 0 ) {
-            return false;
+        if (authorizationHeader == null || authorizationHeader.length() == 0) {
+            return null;
         }
 
         if (log.isDebugEnabled()) {
@@ -301,58 +300,14 @@
         }
 
         String[] prinCred = getPrincipalsAndCredentials(authorizationHeader, request);
-        if ( prinCred == null || prinCred.length < 2 ) {
-            return false;
+        if (prinCred == null || prinCred.length < 2) {
+            return null;
         }
 
         String username = prinCred[0];
         String password = prinCred[1];
 
-        if (log.isDebugEnabled()) {
-            log.debug("Processing login request for username [" + username + "]");
-        }
-
-        AuthenticationToken token = createToken(username, password, request );
-        if ( token != null ) {
-            return executeLogin(token, request, response );
-        }
-
-        //always default to false.  If we've made it to this point in the code, that
-        //means the authentication attempt either never occured, or wasn't successful:
-        return false;
-    }
-
-    /**
-     * Executes a login attmept for the
-     * {@link #getSubject(javax.servlet.ServletRequest, javax.servlet.ServletResponse) currently executing}
-     * <code>Subject</code> using the specified authentication <code>token</code>.
-     * <p/>
-     * The login attempt constitutes calling {@link Subject#login currentSubject.login(token)}.  If the method
-     * call returns successfully, <code>true</code> is returned, <code>false</code> otherwise.
-     * 
-     * @param token the <code>AuthenticationToken</code> representing the submitted username and password.
-     * @param request the incoming ServletRequest
-     * @param response the outgoing ServletResponse
-     * @return <code>true</code> if the authentication attempt is successful, <code>false</code> otherwise.
-     */
-    protected boolean executeLogin( AuthenticationToken token, ServletRequest request, ServletResponse response ) {
-        Subject subject = getSubject(request, response);
-        if ( token != null && subject != null ) {
-            try {
-                subject.login(token);
-                if (log.isDebugEnabled()) {
-                    log.debug("Successfully logged in user [" + token.getPrincipal() + "]");
-                }
-                return true;
-            } catch (AuthenticationException ae) {
-                if (log.isDebugEnabled()) {
-                    log.debug("Unable to log in user [" + token.getPrincipal()+ "]", ae);
-                }
-            }
-        }
-
-        //always default to false - authentication attempt never occurred or wasn't successful:
-        return false;
+        return createToken(username, password, request, response);
     }
 
     /**
@@ -364,16 +319,16 @@
      * {@link #getPrincipalsAndCredentials(String, String) getPrincipalsAndCredentials(scheme,encoded)} method.
      *
      * @param authorizationHeader the authorization header obtained from the request.
-     * @param request the incoming ServletRequest
+     * @param request             the incoming ServletRequest
      * @return the username (index 0)/password pair (index 1) submitted by the user for the given header value and request.
      * @see #getAuthzHeader(javax.servlet.ServletRequest)
      */
-    protected String[] getPrincipalsAndCredentials( String authorizationHeader, ServletRequest request ) {
-        if ( authorizationHeader == null ) {
+    protected String[] getPrincipalsAndCredentials(String authorizationHeader, ServletRequest request) {
+        if (authorizationHeader == null) {
             return null;
         }
         String[] authTokens = authorizationHeader.split(" ");
-        if ( authTokens == null || authTokens.length < 2 ) {
+        if (authTokens == null || authTokens.length < 2) {
             return null;
         }
         return getPrincipalsAndCredentials(authTokens[0], authTokens[1]);
@@ -389,76 +344,14 @@
      * <code>String decoded = Base64.decodeToString(encoded);<br/>
      * return decoded.split(":");</code>
      *
-     * @param scheme the {@link #getAuthcScheme() authcScheme} found in the request
-     * {@link #getAuthzHeader(javax.servlet.ServletRequest) authzHeader}.  It is ignored by this implementation,
-     * but available to overriding implementations should they find it useful.
+     * @param scheme  the {@link #getAuthcScheme() authcScheme} found in the request
+     *                {@link #getAuthzHeader(javax.servlet.ServletRequest) authzHeader}.  It is ignored by this implementation,
+     *                but available to overriding implementations should they find it useful.
      * @param encoded the Base64-encoded username:password value found after the scheme in the header
      * @return the username (index 0)/password (index 1) pair obtained from the encoded header data.
      */
-    protected String[] getPrincipalsAndCredentials( String scheme, String encoded ) {
+    protected String[] getPrincipalsAndCredentials(String scheme, String encoded) {
         String decoded = Base64.decodeToString(encoded);
         return decoded.split(":");
     }
-
-    /**
-     * Creates an AuthenticationToken based on the username and password and incoming request to be submitted to
-     * the {@link Subject#login Subject.login} method for authentication.
-     * <p/>
-     * The default implementation acquires the request's associated
-     * {@link #getInetAddress(javax.servlet.ServletRequest) inetAddress} as well as a potential
-     * {@link #isRememberMeEnabled(javax.servlet.ServletRequest) rememberMe} status, and with the given
-     * <code>username</code> and <code>password</code>, returns a
-     * <code>new {@link org.jsecurity.authc.UsernamePasswordToken UsernamePasswordToken}</code>.  That is:
-     * <p/>
-     * <code>InetAddress addr = getInetAddress(request);<br/>
-     * boolean rememberMe = isRememberMeEnabled(request);<br/>
-     * return new UsernamePasswordToken(username, password, rememberMe, addr );</code>
-     * <p/>
-     * It should be noted that Basic HTTP Authentication does not support any concept of <code>rememberMe</code, but
-     * we still allow subclasses to enable this feature for any given request via the
-     * {@link #isRememberMeEnabled(javax.servlet.ServletRequest) isRememberMeEnabled} method if subclasses wish to
-     * override that method in custom environments.
-     *
-     * @param username the username obtained from the request's 'Authorization' header.
-     * @param password the password obtained from the request's 'Authorization' header.
-     * @param request the incoming ServletRequest.
-     * @return a constructed <code>AuthenticationToken</code> that will be used to execute a login attempt for the
-     * current <code>Subject</code>.
-     */
-    protected AuthenticationToken createToken( String username, String password, ServletRequest request ) {
-        InetAddress addr = getInetAddress(request);
-        boolean rememberMe = isRememberMeEnabled(request);
-        return new UsernamePasswordToken(username, password, rememberMe, addr );
-    }
-
-    /**
-     * Returns the InetAddress associated with the current subject.  This method is primarily provided for use
-     * during construction of an <code>AuthenticationToken</code>.
-     * <p/>
-     * The default implementation merely returns
-     * {@link WebUtils#getInetAddress(javax.servlet.ServletRequest) WebUtils.getInetAddress(request)}.
-     *
-     * @param request the incoming ServletRequest
-     * @return the <code>InetAddress</code> to associate with the login attempt.
-     */
-    protected InetAddress getInetAddress( ServletRequest request ) {
-        return WebUtils.getInetAddress(request);
-    }
-
-    /**
-     * Returns <code>true</code> if &quot;rememberMe&quot; should be enabled for the login attempt associated with the
-     * current <code>request</code>, <code>false</code> otherwise.
-     * <p/>
-     * This implementation always returns <code>false</code> in all cases because Basic HTTP Authentication does not
-     * support the concept of <code>rememberMe</code>.  However, this method is provided as a template hook to
-     * subclasses that might wish to determine <code>rememberMe</code> in a custom mannner based on the current
-     * <code>request</code>.
-     * @param request the incoming ServletRequest
-     * @return <code>true</code> if &quot;rememberMe&quot; should be enabled for the login attempt associated with the
-     * current <code>request</code>, <code>false</code> otherwise.
-     */
-    protected boolean isRememberMeEnabled( ServletRequest request ) {
-        return false;
-    }
-
 }
diff --git a/src/org/jsecurity/web/filter/authc/FormAuthenticationFilter.java b/src/org/jsecurity/web/filter/authc/FormAuthenticationFilter.java
index 7d04b54..8b4bc2b 100644
--- a/src/org/jsecurity/web/filter/authc/FormAuthenticationFilter.java
+++ b/src/org/jsecurity/web/filter/authc/FormAuthenticationFilter.java
@@ -21,14 +21,14 @@
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.jsecurity.authc.AuthenticationException;
+import org.jsecurity.authc.AuthenticationToken;
 import org.jsecurity.authc.UsernamePasswordToken;
+import org.jsecurity.subject.Subject;
 import org.jsecurity.web.WebUtils;
-import static org.jsecurity.web.WebUtils.toHttp;
 
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
-import java.net.InetAddress;
 
 /**
  * Requires the requesting user to be authenticated for the request to continue, and if they are not, forces the user
@@ -57,7 +57,7 @@
  * @see org.jsecurity.web.filter.authc.PassThruAuthenticationFilter
  * @since 0.9
  */
-public class FormAuthenticationFilter extends AuthenticationFilter {
+public class FormAuthenticationFilter extends AuthenticatingFilter {
 
     public static final String DEFAULT_ERROR_KEY_ATTRIBUTE_NAME = "jsecLoginFailure";
 
@@ -65,7 +65,7 @@
     public static final String DEFAULT_PASSWORD_PARAM = "password";
     public static final String DEFAULT_REMEMBER_ME_PARAM = "rememberMe";
 
-    private static final Log log = LogFactory.getLog(FormAuthenticationFilter.class);    
+    private static final Log log = LogFactory.getLog(FormAuthenticationFilter.class);
 
     private String usernameParam = DEFAULT_USERNAME_PARAM;
     private String passwordParam = DEFAULT_PASSWORD_PARAM;
@@ -173,31 +173,31 @@
      * @return <code>true</code> if the request is an HTTP <code>POST</code>, <code>false</code> otherwise.
      */
     protected boolean isLoginSubmission(ServletRequest request, ServletResponse response) {
-        return (request instanceof HttpServletRequest) && toHttp(request).getMethod().equalsIgnoreCase(POST_METHOD);
+        return (request instanceof HttpServletRequest) && WebUtils.toHttp(request).getMethod().equalsIgnoreCase(POST_METHOD);
     }
 
-    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
-        String username = getUsername(request, response);
-        String password = getPassword(request, response);
-        boolean rememberMe = isRememberMe(request, response);
-        InetAddress inet = getInetAddress(request, response);
+    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
+        String username = getUsername(request);
+        String password = getPassword(request);
+        return createToken(username, password, request, response);
+    }
 
-        char[] passwordChars = null;
-        if (password != null) {
-            passwordChars = password.toCharArray();
-        }
+    protected boolean isRememberMe(ServletRequest request) {
+        return WebUtils.isTrue(request, getRememberMeParam());
+    }
 
-        UsernamePasswordToken token = new UsernamePasswordToken(username, passwordChars, rememberMe, inet);
+    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject,
+                                     ServletRequest request, ServletResponse response) throws Exception {
+        issueSuccessRedirect(request, response);
+        //we handled the success redirect directly, prevent the chain from continuing:
+        return false;
+    }
 
-        try {
-            getSubject(request, response).login(token);
-            issueSuccessRedirect(request, response);
-            return false;
-        } catch (AuthenticationException e) {
-            setFailureAttribute(request, e);
-            //login failed, let request continue back to the login page:
-            return true;
-        }
+    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e,
+                                     ServletRequest request, ServletResponse response) {
+        setFailureAttribute(request, e);
+        //login failed, let request continue back to the login page:
+        return true;
     }
 
     protected void setFailureAttribute(ServletRequest request, AuthenticationException ae) {
@@ -205,19 +205,13 @@
         request.setAttribute(getFailureKeyAttribute(), className);
     }
 
-    protected String getUsername(ServletRequest request, ServletResponse response) {
+    protected String getUsername(ServletRequest request) {
         return WebUtils.getCleanParam(request, getUsernameParam());
     }
 
-    protected String getPassword(ServletRequest request, ServletResponse response) {
+    protected String getPassword(ServletRequest request) {
         return WebUtils.getCleanParam(request, getPasswordParam());
     }
 
-    protected boolean isRememberMe(ServletRequest request, ServletResponse response) {
-        return WebUtils.isTrue(request, getRememberMeParam());
-    }
 
-    protected InetAddress getInetAddress(ServletRequest request, ServletResponse response) {
-        return WebUtils.getInetAddress(request);
-    }
 }