001: /*
002: HttpdBase4J: An embeddable Java web server framework that supports HTTP, HTTPS,
003: templated content and serving content from inside a jar or archive.
004: Copyright (C) 2007 Donald Munro
005:
006: This library is free software; you can redistribute it and/or
007: modify it under the terms of the GNU Lesser General Public
008: License as published by the Free Software Foundation; either
009: version 2.1 of the License, or (at your option) any later version.
010:
011: This library is distributed in the hope that it will be useful,
012: but WITHOUT ANY WARRANTY; without even the implied warranty of
013: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: Lesser General Public License for more details.
015:
016: You should have received a copy of the GNU Lesser General Public
017: License along with this library; if not,see http://www.gnu.org/licenses/lgpl.txt
018: */
019:
020: package net.homeip.donaldm.httpdbase4j;
021:
022: import java.sql.Connection;
023: import java.sql.DriverManager;
024: import java.sql.PreparedStatement;
025: import java.sql.ResultSet;
026: import java.sql.SQLException;
027:
028: import com.sun.net.httpserver.BasicAuthenticator;
029:
030: /**
031: * Implements a Basic Authenticator for authentication from a JDBC
032: * source.
033: * @author Donald Munro
034: */
035: public class JdbcBasicAuthenticator extends BasicAuthenticator
036: //============================================================
037: {
038: String m_jdbcUrl = null;
039: String m_sql = null;
040: BasicAuthenticator m_nextAuthenticator = null;
041:
042: /**
043: * Create a JdbcBasicAuthenticator from a JDBC source.
044: * @param realm The authorisation realm.
045: * @param jdbcUrl The URL to connect to the database (assumes that the
046: * JDBC driver has already been loaded.
047: * @param sql The SQL statement to validate the user. Parameters should be
048: * placed in the SQL in the form of a ? character ie as for a
049: * PreparedStatement.<br>
050: * The SQL should read :<br>
051: * <code>
052: * SELECT HASH_FUNCTION(Entered-Password), Hashed-Password <br>
053: * FROM Users-Table <br>
054: * WHERE USER = Entered-User <br>
055: * </code>
056: * where HASH_FUNCTION is the databases password hashing function.<br>
057: *
058: * For example for MySQL you could have:
059: * SELECT PASSWORD(?), Passwd
060: * FROM Users
061: * WHERE User = ?
062: * The row should have been inserted using the hashing function eg
063: * INSERT INTO Users(User, Passwd) VALUES ('idiot', PASSWORD('moron'))
064: *
065: */
066: public JdbcBasicAuthenticator(String realm, String jdbcUrl,
067: String sql)
068: //---------------------------------------------------------------------
069: {
070: super (realm);
071: m_jdbcUrl = jdbcUrl;
072: m_sql = sql;
073: }
074:
075: /**
076: * Create a JdbcBasicAuthenticator from a JDBC source.
077: * @param realm The authorisation realm.
078: * @param jdbcUrl The URL to connect to the database (assumes that the
079: * JDBC driver has already been loaded).
080: * @param sql The SQL statement to validate the user. Parameters should be
081: * placed in the SQL in the form of a ? character ie as for a
082: * PreparedStatement.<br>
083: * The SQL should read :<br>
084: * <code>
085: * SELECT HASH_FUNCTION(Entered-Password), Hashed-Password <br>
086: * FROM Users-Table <br>
087: * WHERE USER = Entered-User <br>
088: * </code>
089: * where HASH_FUNCTION is the databases password hashing function.<br>
090: *
091: * For example for MySQL you could have:
092: * SELECT PASSWORD(?), Passwd
093: * FROM Users
094: * WHERE User = ?
095: * The row should have been inserted using the hashing function eg
096: * INSERT INTO Users(User, Passwd) VALUES ('idiot', PASSWORD('moron'))
097: * @param nextAuthenticator If authorization fails (in the sense of not being
098: * found or an exception occurring, but not in the case of the user being
099: * found but having entered the incorrect password) then forward the
100: * authentication onto this authenticator.
101: */
102: public JdbcBasicAuthenticator(String realm, String jdbcUrl,
103: String sql, BasicAuthenticator nextAuthenticator)
104: //---------------------------------------------------------------------
105: {
106: super (realm);
107: m_jdbcUrl = jdbcUrl;
108: m_sql = sql;
109: m_nextAuthenticator = nextAuthenticator;
110: }
111:
112: /**
113: * Overiding classes can overide this method if the database does not
114: * have a password hashing function or they wish to implement their own
115: * hashing.
116: * @param password The password entered by the user
117: * @return The hashed password. The default implementation simply returns
118: * the incoming password parameter
119: */
120: protected String hashPassword(String password)
121: //-------------------------------------------------
122: {
123: return password;
124: }
125:
126: /**
127: * Overiding classes can overide this method to open a JDBC
128: * connection.
129: * @param jdbcUrl The URL to connect to the database
130: * @return A java.sql.Connection to the database. The default
131: * implementation returns DriverManager.getConnection(jdbcUrl)
132: * @throws java.sql.SQLException
133: */
134: protected Connection openConnection(String jdbcUrl)
135: throws SQLException
136: //---------------------------------------------------------------------
137: {
138: return DriverManager.getConnection(jdbcUrl);
139: }
140:
141: /* (non-Javadoc)
142: * @see com.sun.net.httpserver.BasicAuthenticator#checkCredentials(
143: * java.lang.String, java.lang.String)
144: */
145: @Override
146: public boolean checkCredentials(String userEntered,
147: String passwordEntered)
148: //-----------------------------------------------------------
149: {
150: if ((m_sql == null) || (m_jdbcUrl == null)) {
151: if (m_nextAuthenticator != null)
152: return m_nextAuthenticator.checkCredentials(
153: userEntered, passwordEntered);
154: }
155:
156: Connection connection = null;
157: PreparedStatement pst = null;
158: ResultSet rs = null;
159: try {
160: connection = openConnection(m_jdbcUrl);
161: if (connection == null) {
162: if (m_nextAuthenticator != null)
163: return m_nextAuthenticator.checkCredentials(
164: userEntered, passwordEntered);
165: else
166: return false;
167: }
168: pst = connection.prepareStatement(m_sql);
169: pst.setString(1, passwordEntered);
170: pst.setString(2, userEntered);
171: rs = pst.executeQuery();
172: if (rs.next()) {
173: String passwordHash = hashPassword(rs.getString(1)
174: .trim());
175: String passwdHash = rs.getString(2).trim();
176: return (passwdHash.compareTo(passwordHash) == 0);
177: } else if (m_nextAuthenticator != null)
178: return m_nextAuthenticator.checkCredentials(
179: userEntered, passwordEntered);
180: } catch (Exception e) {
181: System.err.println("Exception chacking password");
182: e.printStackTrace(System.err);
183: if (m_nextAuthenticator != null)
184: return m_nextAuthenticator.checkCredentials(
185: userEntered, passwordEntered);
186: } finally {
187: if (rs != null)
188: try {
189: rs.close();
190: } catch (Exception e) {
191: }
192: if (pst != null) {
193: try {
194: pst.clearParameters();
195: pst.close();
196: } catch (SQLException e) {
197: }
198: }
199: if (connection != null)
200: try {
201: connection.close();
202: } catch (Exception e) {
203: }
204: }
205: return false;
206: }
207: }
|