2 // ========================================================================
3 // Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
19 package org.eclipse.jetty.security;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.sql.Connection;
24 import java.sql.DriverManager;
25 import java.sql.PreparedStatement;
26 import java.sql.ResultSet;
27 import java.sql.SQLException;
28 import java.util.ArrayList;
29 import java.util.List;
30 import java.util.Properties;
32 import org.eclipse.jetty.server.UserIdentity;
33 import org.eclipse.jetty.util.Loader;
34 import org.eclipse.jetty.util.log.Log;
35 import org.eclipse.jetty.util.log.Logger;
36 import org.eclipse.jetty.util.resource.Resource;
37 import org.eclipse.jetty.util.security.Credential;
39 /* ------------------------------------------------------------ */
41 * HashMapped User Realm with JDBC as data source. JDBCLoginService extends
42 * HashULoginService and adds a method to fetch user information from database.
43 * The login() method checks the inherited Map for the user. If the user is not
44 * found, it will fetch details from the database and populate the inherited
45 * Map. It then calls the superclass login() method to perform the actual
46 * authentication. Periodically (controlled by configuration parameter),
47 * internal hashes are cleared. Caching can be disabled by setting cache refresh
48 * interval to zero. Uses one database connection that is initialized at
49 * startup. Reconnect on failures. authenticate() is 'synchronized'.
51 * An example properties file for configuration is in
52 * $JETTY_HOME/etc/jdbcRealm.properties
54 * @version $Id: JDBCLoginService.java 4792 2009-03-18 21:55:52Z gregw $
61 public class JDBCLoginService extends MappedLoginService
63 private static final Logger LOG = Log.getLogger(JDBCLoginService.class);
65 protected String _config;
66 protected String _jdbcDriver;
67 protected String _url;
68 protected String _userName;
69 protected String _password;
70 protected String _userTableKey;
71 protected String _userTablePasswordField;
72 protected String _roleTableRoleField;
73 protected int _cacheTime;
74 protected long _lastHashPurge;
75 protected Connection _con;
76 protected String _userSql;
77 protected String _roleSql;
80 /* ------------------------------------------------------------ */
81 public JDBCLoginService()
86 /* ------------------------------------------------------------ */
87 public JDBCLoginService(String name)
93 /* ------------------------------------------------------------ */
94 public JDBCLoginService(String name, String config)
101 /* ------------------------------------------------------------ */
102 public JDBCLoginService(String name, IdentityService identityService, String config)
106 setIdentityService(identityService);
111 /* ------------------------------------------------------------ */
113 * @see org.eclipse.jetty.security.MappedLoginService#doStart()
116 protected void doStart() throws Exception
118 Properties properties = new Properties();
119 Resource resource = Resource.newResource(_config);
120 try (InputStream in = resource.getInputStream())
124 _jdbcDriver = properties.getProperty("jdbcdriver");
125 _url = properties.getProperty("url");
126 _userName = properties.getProperty("username");
127 _password = properties.getProperty("password");
128 String _userTable = properties.getProperty("usertable");
129 _userTableKey = properties.getProperty("usertablekey");
130 String _userTableUserField = properties.getProperty("usertableuserfield");
131 _userTablePasswordField = properties.getProperty("usertablepasswordfield");
132 String _roleTable = properties.getProperty("roletable");
133 String _roleTableKey = properties.getProperty("roletablekey");
134 _roleTableRoleField = properties.getProperty("roletablerolefield");
135 String _userRoleTable = properties.getProperty("userroletable");
136 String _userRoleTableUserKey = properties.getProperty("userroletableuserkey");
137 String _userRoleTableRoleKey = properties.getProperty("userroletablerolekey");
138 _cacheTime = new Integer(properties.getProperty("cachetime"));
140 if (_jdbcDriver == null || _jdbcDriver.equals("")
144 || _userName.equals("")
148 LOG.warn("UserRealm " + getName() + " has not been properly configured");
152 _userSql = "select " + _userTableKey + "," + _userTablePasswordField + " from " + _userTable + " where " + _userTableUserField + " = ?";
153 _roleSql = "select r." + _roleTableRoleField
159 + _userRoleTableUserKey
164 + _userRoleTableRoleKey;
166 Loader.loadClass(this.getClass(), _jdbcDriver).newInstance();
171 /* ------------------------------------------------------------ */
172 public String getConfig()
177 /* ------------------------------------------------------------ */
179 * Load JDBC connection configuration from properties file.
181 * @param config Filename or url of user properties file.
183 public void setConfig(String config)
186 throw new IllegalStateException("Running");
190 /* ------------------------------------------------------------ */
192 * (re)Connect to database with parameters setup by loadConfig()
194 public void connectDatabase()
198 Class.forName(_jdbcDriver);
199 _con = DriverManager.getConnection(_url, _userName, _password);
201 catch (SQLException e)
203 LOG.warn("UserRealm " + getName() + " could not connect to database; will try later", e);
205 catch (ClassNotFoundException e)
207 LOG.warn("UserRealm " + getName() + " could not connect to database; will try later", e);
211 /* ------------------------------------------------------------ */
213 public UserIdentity login(String username, Object credentials)
215 long now = System.currentTimeMillis();
216 if (now - _lastHashPurge > _cacheTime || _cacheTime == 0)
219 _lastHashPurge = now;
223 return super.login(username,credentials);
226 /* ------------------------------------------------------------ */
228 protected void loadUsers()
232 /* ------------------------------------------------------------ */
234 protected UserIdentity loadUser(String username)
242 throw new SQLException("Can't connect to database");
244 try (PreparedStatement stat1 = _con.prepareStatement(_userSql))
246 stat1.setObject(1, username);
247 try (ResultSet rs1 = stat1.executeQuery())
251 int key = rs1.getInt(_userTableKey);
252 String credentials = rs1.getString(_userTablePasswordField);
253 List<String> roles = new ArrayList<String>();
255 try (PreparedStatement stat2 = _con.prepareStatement(_roleSql))
257 stat2.setInt(1, key);
258 try (ResultSet rs2 = stat2.executeQuery())
261 roles.add(rs2.getString(_roleTableRoleField));
264 return putUser(username, credentials, roles.toArray(new String[roles.size()]));
269 catch (SQLException e)
271 LOG.warn("UserRealm " + getName() + " could not load user information from database", e);
277 /* ------------------------------------------------------------ */
278 protected UserIdentity putUser (String username, String credentials, String[] roles)
280 return putUser(username, Credential.getCredential(credentials),roles);
285 * Close an existing connection
287 private void closeConnection ()
291 if (LOG.isDebugEnabled()) LOG.debug("Closing db connection for JDBCUserRealm");
292 try { _con.close(); }catch (Exception e) {LOG.ignore(e);}