KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > server > security > DigestLogin


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  * Free SoftwareFoundation, Inc.
23  * 59 Temple Place, Suite 330
24  * Boston, MA 02111-1307 USA
25  *
26  * @author Scott Ferguson
27  */

28
29 package com.caucho.server.security;
30
31 import com.caucho.util.Base64;
32 import com.caucho.util.CharBuffer;
33 import com.caucho.util.CharCursor;
34 import com.caucho.util.RandomUtil;
35 import com.caucho.util.StringCharCursor;
36 import com.caucho.xml.XmlChar;
37
38 import javax.servlet.ServletContext JavaDoc;
39 import javax.servlet.ServletException JavaDoc;
40 import javax.servlet.http.HttpServletRequest JavaDoc;
41 import javax.servlet.http.HttpServletResponse JavaDoc;
42 import java.io.IOException JavaDoc;
43 import java.security.Principal JavaDoc;
44 import java.util.logging.Level JavaDoc;
45
46 /**
47  * Implements the "digest" auth-method. Basic uses the
48  * HTTP authentication with WWW-Authenticate and SC_UNAUTHORIZE.
49  */

50 public class DigestLogin extends AbstractLogin {
51   protected String JavaDoc _realm;
52   
53   /**
54    * Sets the login realm.
55    */

56   public void setRealmName(String JavaDoc realm)
57   {
58     _realm = realm;
59   }
60
61   /**
62    * Gets the realm.
63    */

64   public String JavaDoc getRealmName()
65   {
66     return _realm;
67   }
68
69   /**
70    * Returns the authentication type.
71    */

72   public String JavaDoc getAuthType()
73   {
74     return "Digest";
75   }
76   
77   /**
78    * Logs a user in with a user name and a password. Basic authentication
79    * extracts the user and password from the authorization header. If
80    * the user/password is missing, authenticate will send a basic challenge.
81    *
82    * @param request servlet request
83    * @param response servlet response, in case any cookie need sending.
84    * @param application servlet application
85    *
86    * @return the logged in principal on success, null on failure.
87    */

88   public Principal JavaDoc authenticate(HttpServletRequest JavaDoc request,
89                                 HttpServletResponse JavaDoc response,
90                                 ServletContext JavaDoc application)
91     throws ServletException JavaDoc, IOException JavaDoc
92   {
93     Principal JavaDoc user;
94
95     // If the user is already logged-in, return the user
96
user = getAuthenticator().getUserPrincipal(request, response, application);
97     if (user != null)
98       return user;
99     
100     user = getDigestPrincipal(request, response, application);
101
102     if (user != null)
103       return user;
104
105     sendDigestChallenge(response, application);
106     
107     return null;
108   }
109   
110   /**
111    * Returns the current user with the user name and password.
112    *
113    * @param request servlet request
114    * @param response servlet response, in case any cookie need sending.
115    * @param application servlet application
116    *
117    * @return the logged in principal on success, null on failure.
118    */

119   public Principal JavaDoc getUserPrincipal(HttpServletRequest JavaDoc request,
120                                     HttpServletResponse JavaDoc response,
121                                     ServletContext JavaDoc application)
122     throws ServletException JavaDoc
123   {
124     ServletAuthenticator auth = getAuthenticator();
125     
126     Principal JavaDoc user = auth.getUserPrincipal(request, response, application);
127
128     if (user != null)
129       return user;
130     
131     return getDigestPrincipal(request, response, application);
132   }
133
134   /**
135    * Sends a challenge for basic authentication.
136    */

137   protected void sendDigestChallenge(HttpServletResponse JavaDoc res,
138                                      ServletContext JavaDoc application)
139     throws ServletException JavaDoc, IOException JavaDoc
140   {
141     String JavaDoc realm = getRealmName();
142     if (realm == null)
143       realm = "resin";
144
145     CharBuffer cb = CharBuffer.allocate();
146     Base64.encode(cb, getRandomLong(application));
147     String JavaDoc nonce = cb.toString();
148     cb.clear();
149     cb.append("Digest ");
150     cb.append("realm=\"");
151     cb.append(realm);
152     cb.append("\", qop=\"auth\", ");
153     cb.append("nonce=\"");
154     cb.append(nonce);
155     cb.append("\"");
156
157     res.setHeader("WWW-Authenticate", cb.close());
158     
159     res.sendError(res.SC_UNAUTHORIZED);
160   }
161
162   protected long getRandomLong(ServletContext JavaDoc application)
163   {
164     return RandomUtil.getRandomLong();
165   }
166
167   /**
168    * Returns the principal from a basic authentication
169    *
170    * @param auth the authenticator for this application.
171    */

172   protected Principal JavaDoc getDigestPrincipal(HttpServletRequest JavaDoc request,
173                                          HttpServletResponse JavaDoc response,
174                                          ServletContext JavaDoc application)
175     throws ServletException JavaDoc
176   {
177     String JavaDoc value = request.getHeader("authorization");
178
179     if (value == null)
180       return null;
181
182     String JavaDoc username = null;
183     String JavaDoc realm = null;
184     String JavaDoc uri = null;
185     String JavaDoc nonce = null;
186     String JavaDoc cnonce = null;
187     String JavaDoc nc = null;
188     String JavaDoc qop = null;
189     String JavaDoc digest = null;
190
191     CharCursor cursor = new StringCharCursor(value);
192
193     String JavaDoc key = scanKey(cursor);
194
195     if (! "Digest".equalsIgnoreCase(key))
196       return null;
197       
198     while ((key = scanKey(cursor)) != null) {
199       value = scanValue(cursor);
200
201       if (key.equals("username"))
202         username = value;
203       else if (key.equals("realm"))
204         realm = value;
205       else if (key.equals("uri"))
206         uri = value;
207       else if (key.equals("nonce"))
208         nonce = value;
209       else if (key.equals("response"))
210         digest = value;
211       else if (key.equals("cnonce"))
212         cnonce = value;
213       else if (key.equals("nc"))
214         nc = value;
215       else if (key.equals("qop"))
216         qop = value;
217     }
218
219     byte []clientDigest = decodeDigest(digest);
220
221     if (clientDigest == null || username == null ||
222         uri == null || nonce == null)
223       return null;
224
225     ServletAuthenticator auth = getAuthenticator();
226     Principal JavaDoc principal = auth.loginDigest(request, response, application,
227                                            username, realm, nonce, uri,
228                                            qop, nc, cnonce,
229                                            clientDigest);
230     
231     if (log.isLoggable(Level.FINE))
232       log.fine("digest: " + username + " -> " + principal);
233
234     return principal;
235   }
236
237   protected byte []decodeDigest(String JavaDoc digest)
238   {
239     if (digest == null)
240       return null;
241     
242     int len = (digest.length() + 1) / 2;
243     byte []clientDigest = new byte[len];
244
245     for (int i = 0; i + 1 < digest.length(); i += 2) {
246       int ch1 = digest.charAt(i);
247       int ch2 = digest.charAt(i + 1);
248
249       int b = 0;
250       if (ch1 >= '0' && ch1 <= '9')
251         b += ch1 - '0';
252       else if (ch1 >= 'a' && ch1 <= 'f')
253         b += ch1 - 'a' + 10;
254
255       b *= 16;
256       
257       if (ch2 >= '0' && ch2 <= '9')
258         b += ch2 - '0';
259       else if (ch2 >= 'a' && ch2 <= 'f')
260         b += ch2 - 'a' + 10;
261
262       clientDigest[i / 2] = (byte) b;
263     }
264
265     return clientDigest;
266   }
267
268   protected String JavaDoc scanKey(CharCursor cursor)
269     throws ServletException JavaDoc
270   {
271     int ch;
272     while (XmlChar.isWhitespace((ch = cursor.current())) || ch == ',') {
273       cursor.next();
274     }
275
276     ch = cursor.current();
277     if (ch == cursor.DONE)
278       return null;
279     
280     if (! XmlChar.isNameStart(ch))
281       throw new ServletException JavaDoc("bad key: " + (char) ch + " " + cursor);
282
283     CharBuffer cb = CharBuffer.allocate();
284     while (XmlChar.isNameChar(ch = cursor.read())) {
285       cb.append((char) ch);
286     }
287     if (ch != cursor.DONE)
288       cursor.previous();
289
290     return cb.close();
291   }
292
293   protected String JavaDoc scanValue(CharCursor cursor)
294     throws ServletException JavaDoc
295   {
296     int ch;
297     skipWhitespace(cursor);
298
299     ch = cursor.read();
300     if (ch != '=')
301       throw new ServletException JavaDoc("expected '='");
302
303     skipWhitespace(cursor);
304     
305     CharBuffer cb = CharBuffer.allocate();
306     
307     ch = cursor.read();
308     if (ch == '"')
309       while ((ch = cursor.read()) != cursor.DONE && ch != '"')
310         cb.append((char) ch);
311     else {
312       for (;
313            ch != cursor.DONE && ch != ',' && ! XmlChar.isWhitespace(ch);
314            ch = cursor.read())
315         cb.append((char) ch);
316
317       if (ch != cursor.DONE)
318     cursor.previous();
319     }
320
321     return cb.close();
322   }
323
324   protected void skipWhitespace(CharCursor cursor)
325   {
326     while (XmlChar.isWhitespace(cursor.current())) {
327       cursor.next();
328     }
329   }
330 }
331
Popular Tags