KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > catalina > authenticator > FormAuthenticator


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

17
18
19 package org.apache.catalina.authenticator;
20
21
22 import java.io.IOException JavaDoc;
23 import java.io.InputStream JavaDoc;
24 import java.security.Principal JavaDoc;
25 import java.util.Enumeration JavaDoc;
26 import java.util.Iterator JavaDoc;
27 import java.util.Locale JavaDoc;
28
29 import javax.servlet.RequestDispatcher JavaDoc;
30 import javax.servlet.http.Cookie JavaDoc;
31 import javax.servlet.http.HttpServletResponse JavaDoc;
32
33 import org.apache.catalina.Realm;
34 import org.apache.catalina.Session;
35 import org.apache.catalina.connector.Request;
36 import org.apache.catalina.connector.Response;
37 import org.apache.catalina.deploy.LoginConfig;
38 import org.apache.commons.logging.Log;
39 import org.apache.commons.logging.LogFactory;
40 import org.apache.coyote.ActionCode;
41 import org.apache.tomcat.util.buf.ByteChunk;
42 import org.apache.tomcat.util.buf.CharChunk;
43 import org.apache.tomcat.util.buf.MessageBytes;
44 import org.apache.tomcat.util.http.MimeHeaders;
45
46
47 /**
48  * An <b>Authenticator</b> and <b>Valve</b> implementation of FORM BASED
49  * Authentication, as described in the Servlet API Specification, Version 2.2.
50  *
51  * @author Craig R. McClanahan
52  * @author Remy Maucherat
53  * @version $Revision: 467222 $ $Date: 2006-10-24 05:17:11 +0200 (mar., 24 oct. 2006) $
54  */

55
56 public class FormAuthenticator
57     extends AuthenticatorBase {
58     
59     private static Log log = LogFactory.getLog(FormAuthenticator.class);
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 '"
193                           + session.getIdInternal()
194                           + "'");
195             principal = (Principal JavaDoc)
196                 session.getNote(Constants.FORM_PRINCIPAL_NOTE);
197             register(request, response, principal, Constants.FORM_METHOD,
198                      (String JavaDoc) session.getNote(Constants.SESS_USERNAME_NOTE),
199                      (String JavaDoc) session.getNote(Constants.SESS_PASSWORD_NOTE));
200             // If we're caching principals we no longer need the username
201
// and password in the session, so remove them
202
if (cache) {
203                 session.removeNote(Constants.SESS_USERNAME_NOTE);
204                 session.removeNote(Constants.SESS_PASSWORD_NOTE);
205             }
206             if (restoreRequest(request, session)) {
207                 if (log.isDebugEnabled())
208                     log.debug("Proceed to restored request");
209                 return (true);
210             } else {
211                 if (log.isDebugEnabled())
212                     log.debug("Restore of original request failed");
213                 response.sendError(HttpServletResponse.SC_BAD_REQUEST);
214                 return (false);
215             }
216         }
217
218         // Acquire references to objects we will need to evaluate
219
MessageBytes uriMB = MessageBytes.newInstance();
220         CharChunk uriCC = uriMB.getCharChunk();
221         uriCC.setLimit(-1);
222         String JavaDoc contextPath = request.getContextPath();
223         String JavaDoc requestURI = request.getDecodedRequestURI();
224         response.setContext(request.getContext());
225
226         // Is this the action request from the login page?
227
boolean loginAction =
228             requestURI.startsWith(contextPath) &&
229             requestURI.endsWith(Constants.FORM_ACTION);
230
231         // No -- Save this request and redirect to the form login page
232
if (!loginAction) {
233             session = request.getSessionInternal(true);
234             if (log.isDebugEnabled())
235                 log.debug("Save request in session '" + session.getIdInternal() + "'");
236             try {
237                 saveRequest(request, session);
238             } catch (IOException JavaDoc ioe) {
239                 log.debug("Request body too big to save during authentication");
240                 response.sendError(HttpServletResponse.SC_FORBIDDEN,
241                         sm.getString("authenticator.requestBodyTooBig"));
242                 return (false);
243             }
244             forwardToLoginPage(request, response, config);
245             return (false);
246         }
247
248         // Yes -- Validate the specified credentials and redirect
249
// to the error page if they are not correct
250
Realm realm = context.getRealm();
251         if (characterEncoding != null) {
252             request.setCharacterEncoding(characterEncoding);
253         }
254         String JavaDoc username = request.getParameter(Constants.FORM_USERNAME);
255         String JavaDoc password = request.getParameter(Constants.FORM_PASSWORD);
256         if (log.isDebugEnabled())
257             log.debug("Authenticating username '" + username + "'");
258         principal = realm.authenticate(username, password);
259         if (principal == null) {
260             forwardToErrorPage(request, response, config);
261             return (false);
262         }
263
264         if (log.isDebugEnabled())
265             log.debug("Authentication of '" + username + "' was successful");
266
267         if (session == null)
268             session = request.getSessionInternal(false);
269         if (session == null) {
270             if (containerLog.isDebugEnabled())
271                 containerLog.debug
272                     ("User took so long to log on the session expired");
273             response.sendError(HttpServletResponse.SC_REQUEST_TIMEOUT,
274                                sm.getString("authenticator.sessionExpired"));
275             return (false);
276         }
277
278         // Save the authenticated Principal in our session
279
session.setNote(Constants.FORM_PRINCIPAL_NOTE, principal);
280
281         // Save the username and password as well
282
session.setNote(Constants.SESS_USERNAME_NOTE, username);
283         session.setNote(Constants.SESS_PASSWORD_NOTE, password);
284
285         // Redirect the user to the original request URI (which will cause
286
// the original request to be restored)
287
requestURI = savedRequestURL(session);
288         if (log.isDebugEnabled())
289             log.debug("Redirecting to original '" + requestURI + "'");
290         if (requestURI == null)
291             response.sendError(HttpServletResponse.SC_BAD_REQUEST,
292                                sm.getString("authenticator.formlogin"));
293         else
294             response.sendRedirect(response.encodeRedirectURL(requestURI));
295         return (false);
296
297     }
298
299
300     // ------------------------------------------------------ Protected Methods
301

302
303     /**
304      * Called to forward to the login page
305      *
306      * @param request Request we are processing
307      * @param response Response we are creating
308      * @param config Login configuration describing how authentication
309      * should be performed
310      */

311     protected void forwardToLoginPage(Request request, Response JavaDoc response, LoginConfig config) {
312         RequestDispatcher JavaDoc disp =
313             context.getServletContext().getRequestDispatcher
314             (config.getLoginPage());
315         try {
316             disp.forward(request.getRequest(), response.getResponse());
317             response.finishResponse();
318         } catch (Throwable JavaDoc t) {
319             log.warn("Unexpected error forwarding to login page", t);
320         }
321     }
322
323
324     /**
325      * Called to forward to the error page
326      *
327      * @param request Request we are processing
328      * @param response Response we are creating
329      * @param config Login configuration describing how authentication
330      * should be performed
331      */

332     protected void forwardToErrorPage(Request request, Response JavaDoc response, LoginConfig config) {
333         RequestDispatcher JavaDoc disp =
334             context.getServletContext().getRequestDispatcher
335             (config.getErrorPage());
336         try {
337             disp.forward(request.getRequest(), response.getResponse());
338         } catch (Throwable JavaDoc t) {
339             log.warn("Unexpected error forwarding to error page", t);
340         }
341     }
342
343
344     /**
345      * Does this request match the saved one (so that it must be the redirect
346      * we signalled after successful authentication?
347      *
348      * @param request The request to be verified
349      */

350     protected boolean matchRequest(Request request) {
351
352       // Has a session been created?
353
Session session = request.getSessionInternal(false);
354       if (session == null)
355           return (false);
356
357       // Is there a saved request?
358
SavedRequest sreq = (SavedRequest)
359           session.getNote(Constants.FORM_REQUEST_NOTE);
360       if (sreq == null)
361           return (false);
362
363       // Is there a saved principal?
364
if (session.getNote(Constants.FORM_PRINCIPAL_NOTE) == null)
365           return (false);
366
367       // Does the request URI match?
368
String JavaDoc requestURI = request.getRequestURI();
369       if (requestURI == null)
370           return (false);
371       return (requestURI.equals(request.getRequestURI()));
372
373     }
374
375
376     /**
377      * Restore the original request from information stored in our session.
378      * If the original request is no longer present (because the session
379      * timed out), return <code>false</code>; otherwise, return
380      * <code>true</code>.
381      *
382      * @param request The request to be restored
383      * @param session The session containing the saved information
384      */

385     protected boolean restoreRequest(Request request, Session session)
386         throws IOException JavaDoc {
387
388         // Retrieve and remove the SavedRequest object from our session
389
SavedRequest saved = (SavedRequest)
390             session.getNote(Constants.FORM_REQUEST_NOTE);
391         session.removeNote(Constants.FORM_REQUEST_NOTE);
392         session.removeNote(Constants.FORM_PRINCIPAL_NOTE);
393         if (saved == null)
394             return (false);
395
396         // Modify our current request to reflect the original one
397
request.clearCookies();
398         Iterator JavaDoc cookies = saved.getCookies();
399         while (cookies.hasNext()) {
400             request.addCookie((Cookie JavaDoc) cookies.next());
401         }
402
403         MimeHeaders rmh = request.getCoyoteRequest().getMimeHeaders();
404         rmh.recycle();
405         Iterator JavaDoc names = saved.getHeaderNames();
406         while (names.hasNext()) {
407             String JavaDoc name = (String JavaDoc) names.next();
408             Iterator JavaDoc values = saved.getHeaderValues(name);
409             while (values.hasNext()) {
410                 rmh.addValue(name).setString( (String JavaDoc)values.next() );
411             }
412         }
413         
414         request.clearLocales();
415         Iterator JavaDoc locales = saved.getLocales();
416         while (locales.hasNext()) {
417             request.addLocale((Locale JavaDoc) locales.next());
418         }
419         
420         request.getCoyoteRequest().getParameters().recycle();
421         
422         if ("POST".equalsIgnoreCase(saved.getMethod())) {
423             ByteChunk body = saved.getBody();
424             
425             if (body != null) {
426                 request.getCoyoteRequest().action
427                     (ActionCode.ACTION_REQ_SET_BODY_REPLAY, body);
428     
429                 // Set content type
430
MessageBytes contentType = MessageBytes.newInstance();
431                 contentType.setString("application/x-www-form-urlencoded");
432                 request.getCoyoteRequest().setContentType(contentType);
433             }
434         }
435         request.getCoyoteRequest().method().setString(saved.getMethod());
436
437         request.getCoyoteRequest().queryString().setString
438             (saved.getQueryString());
439
440         request.getCoyoteRequest().requestURI().setString
441             (saved.getRequestURI());
442         return (true);
443
444     }
445
446
447     /**
448      * Save the original request information into our session.
449      *
450      * @param request The request to be saved
451      * @param session The session to contain the saved information
452      * @throws IOException
453      */

454     protected void saveRequest(Request request, Session session)
455         throws IOException JavaDoc {
456
457         // Create and populate a SavedRequest object for this request
458
SavedRequest saved = new SavedRequest();
459         Cookie JavaDoc cookies[] = request.getCookies();
460         if (cookies != null) {
461             for (int i = 0; i < cookies.length; i++)
462                 saved.addCookie(cookies[i]);
463         }
464         Enumeration JavaDoc names = request.getHeaderNames();
465         while (names.hasMoreElements()) {
466             String JavaDoc name = (String JavaDoc) names.nextElement();
467             Enumeration JavaDoc values = request.getHeaders(name);
468             while (values.hasMoreElements()) {
469                 String JavaDoc value = (String JavaDoc) values.nextElement();
470                 saved.addHeader(name, value);
471             }
472         }
473         Enumeration JavaDoc locales = request.getLocales();
474         while (locales.hasMoreElements()) {
475             Locale JavaDoc locale = (Locale JavaDoc) locales.nextElement();
476             saved.addLocale(locale);
477         }
478
479         if ("POST".equalsIgnoreCase(request.getMethod())) {
480             ByteChunk body = new ByteChunk();
481             body.setLimit(request.getConnector().getMaxSavePostSize());
482
483             byte[] buffer = new byte[4096];
484             int bytesRead;
485             InputStream JavaDoc is = request.getInputStream();
486         
487             while ( (bytesRead = is.read(buffer) ) >= 0) {
488                 body.append(buffer, 0, bytesRead);
489             }
490             saved.setBody(body);
491         }
492
493         saved.setMethod(request.getMethod());
494         saved.setQueryString(request.getQueryString());
495         saved.setRequestURI(request.getRequestURI());
496
497         // Stash the SavedRequest in our session for later use
498
session.setNote(Constants.FORM_REQUEST_NOTE, saved);
499
500     }
501
502
503     /**
504      * Return the request URI (with the corresponding query string, if any)
505      * from the saved request so that we can redirect to it.
506      *
507      * @param session Our current session
508      */

509     protected String JavaDoc savedRequestURL(Session session) {
510
511         SavedRequest saved =
512             (SavedRequest) session.getNote(Constants.FORM_REQUEST_NOTE);
513         if (saved == null)
514             return (null);
515         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(saved.getRequestURI());
516         if (saved.getQueryString() != null) {
517             sb.append('?');
518             sb.append(saved.getQueryString());
519         }
520         return (sb.toString());
521
522     }
523
524
525 }
526
Popular Tags