KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > web > tomcat > security > FormAuthenticator


1 /*
2  * Copyright 1999,2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.jboss.web.tomcat.security;
17
18 import java.io.IOException JavaDoc;
19 import java.security.Principal JavaDoc;
20 import java.util.Enumeration JavaDoc;
21 import java.util.Iterator JavaDoc;
22 import java.util.Locale JavaDoc;
23 import java.util.Map JavaDoc;
24
25 import javax.servlet.RequestDispatcher JavaDoc;
26 import javax.servlet.http.Cookie JavaDoc;
27 import javax.servlet.http.HttpServletResponse JavaDoc;
28
29 import org.apache.catalina.Realm;
30 import org.apache.catalina.Session;
31 import org.apache.catalina.authenticator.AuthenticatorBase;
32 import org.apache.catalina.authenticator.Constants;
33 import org.apache.catalina.authenticator.SavedRequest;
34 import org.apache.catalina.connector.Request;
35 import org.apache.catalina.connector.Response;
36 import org.apache.catalina.deploy.LoginConfig;
37 import org.apache.commons.logging.Log;
38 import org.apache.commons.logging.LogFactory;
39 import org.apache.tomcat.util.buf.CharChunk;
40 import org.apache.tomcat.util.buf.MessageBytes;
41
42
43 /**
44  * An <b>Authenticator</b> and <b>Valve</b> implementation of FORM BASED
45  * Authentication, as described in the Servlet API Specification, Version 2.2.
46  *
47  * The refactored FormAuthenticator suggested in
48  * http://issues.apache.org/bugzilla/show_bug.cgi?id=36136
49  *
50  * @author Craig R. McClanahan
51  * @author Remy Maucherat
52  * @version $Revision: 45539 $ $Date: 2006-06-06 12:48:51 -0400 (Tue, 06 Jun 2006) $
53  */

54
55 public class FormAuthenticator
56     extends AuthenticatorBase {
57     private static Log log = LogFactory.getLog(FormAuthenticator.class);
58
59
60
61     // ----------------------------------------------------- Instance Variables
62

63
64     /**
65      * Descriptive information about this implementation.
66      */

67     protected static final String JavaDoc info =
68         "org.apache.catalina.authenticator.FormAuthenticator/1.0";
69
70     /**
71      * Character encoding to use to read the username and password parameters
72      * from the request. If not set, the encoding of the request body will be
73      * used.
74      */

75     protected String JavaDoc characterEncoding = null;
76
77
78     // ------------------------------------------------------------- Properties
79

80
81     /**
82      * Return descriptive information about this Valve implementation.
83      */

84     public String JavaDoc getInfo() {
85
86         return (info);
87
88     }
89
90
91     /**
92      * Return the character encoding to use to read the username and password.
93      */

94     public String JavaDoc getCharacterEncoding() {
95         return characterEncoding;
96     }
97
98     
99     /**
100      * Set the character encoding to be used to read the username and password.
101      */

102     public void setCharacterEncoding(String JavaDoc encoding) {
103         characterEncoding = encoding;
104     }
105
106
107     // --------------------------------------------------------- Public Methods
108

109
110     /**
111      * Authenticate the user making this request, based on the specified
112      * login configuration. Return <code>true</code> if any specified
113      * constraint has been satisfied, or <code>false</code> if we have
114      * created a response challenge already.
115      *
116      * @param request Request we are processing
117      * @param response Response we are creating
118      * @param config Login configuration describing how authentication
119      * should be performed
120      *
121      * @exception IOException if an input/output error occurs
122      */

123     public boolean authenticate(Request request,
124                                 Response JavaDoc response,
125                                 LoginConfig config)
126         throws IOException JavaDoc {
127
128         // References to objects we will need later
129
Session session = null;
130
131         // Have we already authenticated someone?
132
Principal JavaDoc principal = request.getUserPrincipal();
133         String JavaDoc ssoId = (String JavaDoc) request.getNote(Constants.REQ_SSOID_NOTE);
134         if (principal != null) {
135             if (log.isDebugEnabled())
136                 log.debug("Already authenticated '" +
137                     principal.getName() + "'");
138             // Associate the session with any existing SSO session
139
if (ssoId != null)
140                 associate(ssoId, request.getSessionInternal(true));
141             return (true);
142         }
143
144         // Is there an SSO session against which we can try to reauthenticate?
145
if (ssoId != null) {
146             if (log.isDebugEnabled())
147                 log.debug("SSO Id " + ssoId + " set; attempting " +
148                           "reauthentication");
149             // Try to reauthenticate using data cached by SSO. If this fails,
150
// either the original SSO logon was of DIGEST or SSL (which
151
// we can't reauthenticate ourselves because there is no
152
// cached username and password), or the realm denied
153
// the user's reauthentication for some reason.
154
// In either case we have to prompt the user for a logon */
155
if (reauthenticateFromSSO(ssoId, request))
156                 return true;
157         }
158
159         // Have we authenticated this user before but have caching disabled?
160
if (!cache) {
161             session = request.getSessionInternal(true);
162             if (log.isDebugEnabled())
163                 log.debug("Checking for reauthenticate in session " + session);
164             String JavaDoc username =
165                 (String JavaDoc) session.getNote(Constants.SESS_USERNAME_NOTE);
166             String JavaDoc password =
167                 (String JavaDoc) session.getNote(Constants.SESS_PASSWORD_NOTE);
168             if ((username != null) && (password != null)) {
169                 if (log.isDebugEnabled())
170                     log.debug("Reauthenticating username '" + username + "'");
171                 principal =
172                     context.getRealm().authenticate(username, password);
173                 if (principal != null) {
174                     session.setNote(Constants.FORM_PRINCIPAL_NOTE, principal);
175                     if (!matchRequest(request)) {
176                         register(request, response, principal,
177                                  Constants.FORM_METHOD,
178                                  username, password);
179                         return (true);
180                     }
181                 }
182                 if (log.isDebugEnabled())
183                     log.debug("Reauthentication failed, proceed normally");
184             }
185         }
186
187         // Is this the re-submit of the original request URI after successful
188
// authentication? If so, forward the *original* request instead.
189
if (matchRequest(request)) {
190             session = request.getSessionInternal(true);
191             if (log.isDebugEnabled())
192                 log.debug("Restore request from session '" + session.getId()
193                           + "'");
194             principal = (Principal JavaDoc)
195                 session.getNote(Constants.FORM_PRINCIPAL_NOTE);
196             register(request, response, principal, Constants.FORM_METHOD,
197                      (String JavaDoc) session.getNote(Constants.SESS_USERNAME_NOTE),
198                      (String JavaDoc) session.getNote(Constants.SESS_PASSWORD_NOTE));
199             // If we're caching principals we no longer need the username
200
// and password in the session, so remove them
201
if (cache) {
202                 session.removeNote(Constants.SESS_USERNAME_NOTE);
203                 session.removeNote(Constants.SESS_PASSWORD_NOTE);
204             }
205             if (restoreRequest(request, session)) {
206                 if (log.isDebugEnabled())
207                     log.debug("Proceed to restored request");
208                 return (true);
209             } else {
210                 if (log.isDebugEnabled())
211                     log.debug("Restore of original request failed");
212                 response.sendError(HttpServletResponse.SC_BAD_REQUEST);
213                 return (false);
214             }
215         }
216
217         // Acquire references to objects we will need to evaluate
218
MessageBytes uriMB = MessageBytes.newInstance();
219         CharChunk uriCC = uriMB.getCharChunk();
220         uriCC.setLimit(-1);
221         String JavaDoc contextPath = request.getContextPath();
222         String JavaDoc requestURI = request.getDecodedRequestURI();
223         response.setContext(request.getContext());
224
225         // Is this the action request from the login page?
226
boolean loginAction =
227             requestURI.startsWith(contextPath) &&
228             requestURI.endsWith(Constants.FORM_ACTION);
229
230         // No -- Save this request and redirect to the form login page
231
if (!loginAction) {
232             session = request.getSessionInternal(true);
233             if (log.isDebugEnabled())
234                 log.debug("Save request in session '" + session.getId() + "'");
235             saveRequest(request, session);
236            forwardToLoginPage(request, response, config);
237            return (false);
238         }
239
240         // Yes -- Validate the specified credentials and redirect
241
// to the error page if they are not correct
242
Realm realm = context.getRealm();
243         if (characterEncoding != null) {
244             request.setCharacterEncoding(characterEncoding);
245         }
246         String JavaDoc username = request.getParameter(Constants.FORM_USERNAME);
247         String JavaDoc password = request.getParameter(Constants.FORM_PASSWORD);
248         if (log.isDebugEnabled())
249             log.debug("Authenticating username '" + username + "'");
250         principal = realm.authenticate(username, password);
251         if (principal == null) {
252            forwardToErrorPage(request, response, config);
253            return (false);
254         }
255
256         if (log.isDebugEnabled())
257             log.debug("Authentication of '" + username + "' was successful");
258
259         if (session == null)
260             session = request.getSessionInternal(false);
261         if (session == null) {
262             if (containerLog.isDebugEnabled())
263                 containerLog.debug("User took so long to log on the session expired");
264             response.sendError(HttpServletResponse.SC_REQUEST_TIMEOUT,
265                                sm.getString("authenticator.sessionExpired"));
266             return (false);
267         }
268
269         // Save the authenticated Principal in our session
270
session.setNote(Constants.FORM_PRINCIPAL_NOTE, principal);
271
272         // Save the username and password as well
273
session.setNote(Constants.SESS_USERNAME_NOTE, username);
274         session.setNote(Constants.SESS_PASSWORD_NOTE, password);
275
276         // Redirect the user to the original request URI (which will cause
277
// the original request to be restored)
278
requestURI = savedRequestURL(session);
279         if (log.isDebugEnabled())
280             log.debug("Redirecting to original '" + requestURI + "'");
281         if (requestURI == null)
282             response.sendError(HttpServletResponse.SC_BAD_REQUEST,
283                                sm.getString("authenticator.formlogin"));
284         else
285             response.sendRedirect(response.encodeRedirectURL(requestURI));
286         return (false);
287
288     }
289
290    // ------------------------------------------------------ Protected Methods
291

292
293    protected void forwardToErrorPage(Request request, Response JavaDoc response, LoginConfig config)
294    {
295       RequestDispatcher JavaDoc disp =
296           context.getServletContext().getRequestDispatcher
297           (config.getErrorPage());
298       try {
299           disp.forward(request.getRequest(), response.getResponse());
300       } catch (Throwable JavaDoc t) {
301           log.warn("Unexpected error forwarding to error page", t);
302       }
303    }
304
305    protected void forwardToLoginPage(Request request, Response JavaDoc response, LoginConfig config)
306    {
307       RequestDispatcher JavaDoc disp =
308           context.getServletContext().getRequestDispatcher
309           (config.getLoginPage());
310       try {
311           disp.forward(request.getRequest(), response.getResponse());
312           response.finishResponse();
313       } catch (Throwable JavaDoc t) {
314           log.warn("Unexpected error forwarding to login page", t);
315       }
316    }
317
318     /**
319      * Does this request match the saved one (so that it must be the redirect
320      * we signalled after successful authentication?
321      *
322      * @param request The request to be verified
323      */

324     protected boolean matchRequest(Request request) {
325
326       // Has a session been created?
327
Session session = request.getSessionInternal(false);
328       if (session == null)
329           return (false);
330
331       // Is there a saved request?
332
SavedRequest sreq = (SavedRequest)
333           session.getNote(Constants.FORM_REQUEST_NOTE);
334       if (sreq == null)
335           return (false);
336
337       // Is there a saved principal?
338
if (session.getNote(Constants.FORM_PRINCIPAL_NOTE) == null)
339           return (false);
340
341       // Does the request URI match?
342
String JavaDoc requestURI = request.getRequestURI();
343       if (requestURI == null)
344           return (false);
345       return (requestURI.equals(sreq.getRequestURI()));
346
347     }
348
349
350     /**
351      * Restore the original request from information stored in our session.
352      * If the original request is no longer present (because the session
353      * timed out), return <code>false</code>; otherwise, return
354      * <code>true</code>.
355      *
356      * @param request The request to be restored
357      * @param session The session containing the saved information
358      */

359     protected boolean restoreRequest(Request request, Session session) {
360
361         // Retrieve and remove the SavedRequest object from our session
362
SavedRequest saved = (SavedRequest)
363             session.getNote(Constants.FORM_REQUEST_NOTE);
364         session.removeNote(Constants.FORM_REQUEST_NOTE);
365         session.removeNote(Constants.FORM_PRINCIPAL_NOTE);
366         if (saved == null)
367             return (false);
368
369         // Modify our current request to reflect the original one
370
request.clearCookies();
371         Iterator JavaDoc cookies = saved.getCookies();
372         while (cookies.hasNext()) {
373             request.addCookie((Cookie JavaDoc) cookies.next());
374         }
375         request.clearHeaders();
376         Iterator JavaDoc names = saved.getHeaderNames();
377         while (names.hasNext()) {
378             String JavaDoc name = (String JavaDoc) names.next();
379             Iterator JavaDoc values = saved.getHeaderValues(name);
380             while (values.hasNext()) {
381                 request.addHeader(name, (String JavaDoc) values.next());
382             }
383         }
384         request.clearLocales();
385         Iterator JavaDoc locales = saved.getLocales();
386         while (locales.hasNext()) {
387             request.addLocale((Locale JavaDoc) locales.next());
388         }
389         request.clearParameters();
390         if ("POST".equalsIgnoreCase(saved.getMethod())) {
391             Iterator JavaDoc paramNames = saved.getParameterNames();
392             while (paramNames.hasNext()) {
393                 String JavaDoc paramName = (String JavaDoc) paramNames.next();
394                 String JavaDoc paramValues[] =
395                     saved.getParameterValues(paramName);
396                 request.addParameter(paramName, paramValues);
397             }
398         }
399         request.setMethod(saved.getMethod());
400         request.setQueryString(saved.getQueryString());
401         request.setRequestURI(saved.getRequestURI());
402         return (true);
403
404     }
405
406
407     /**
408      * Save the original request information into our session.
409      *
410      * @param request The request to be saved
411      * @param session The session to contain the saved information
412      */

413     private void saveRequest(Request request, Session session) {
414
415         // Create and populate a SavedRequest object for this request
416
SavedRequest saved = new SavedRequest();
417         Cookie JavaDoc cookies[] = request.getCookies();
418         if (cookies != null) {
419             for (int i = 0; i < cookies.length; i++)
420                 saved.addCookie(cookies[i]);
421         }
422         Enumeration JavaDoc names = request.getHeaderNames();
423         while (names.hasMoreElements()) {
424             String JavaDoc name = (String JavaDoc) names.nextElement();
425             Enumeration JavaDoc values = request.getHeaders(name);
426             while (values.hasMoreElements()) {
427                 String JavaDoc value = (String JavaDoc) values.nextElement();
428                 saved.addHeader(name, value);
429             }
430         }
431         Enumeration JavaDoc locales = request.getLocales();
432         while (locales.hasMoreElements()) {
433             Locale JavaDoc locale = (Locale JavaDoc) locales.nextElement();
434             saved.addLocale(locale);
435         }
436         Map JavaDoc parameters = request.getParameterMap();
437         Iterator JavaDoc paramNames = parameters.keySet().iterator();
438         while (paramNames.hasNext()) {
439             String JavaDoc paramName = (String JavaDoc) paramNames.next();
440             String JavaDoc paramValues[] = (String JavaDoc[]) parameters.get(paramName);
441             saved.addParameter(paramName, paramValues);
442         }
443         saved.setMethod(request.getMethod());
444         saved.setQueryString(request.getQueryString());
445         saved.setRequestURI(request.getRequestURI());
446
447         // Stash the SavedRequest in our session for later use
448
session.setNote(Constants.FORM_REQUEST_NOTE, saved);
449
450     }
451
452
453     /**
454      * Return the request URI (with the corresponding query string, if any)
455      * from the saved request so that we can redirect to it.
456      *
457      * @param session Our current session
458      */

459     private String JavaDoc savedRequestURL(Session session) {
460
461         SavedRequest saved =
462             (SavedRequest) session.getNote(Constants.FORM_REQUEST_NOTE);
463         if (saved == null)
464             return (null);
465         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(saved.getRequestURI());
466         if (saved.getQueryString() != null) {
467             sb.append('?');
468             sb.append(saved.getQueryString());
469         }
470         return (sb.toString());
471
472     }
473
474
475 }
476
Popular Tags