View Javadoc

    1   // ========================================================================
    2   // $Id: JDBCUserRealm.java 3345 2008-07-22 05:58:07Z dyu $
    3   // Copyright 2003-2004 Mort Bay Consulting Pty. Ltd.
    4   // ------------------------------------------------------------------------
    5   // Licensed under the Apache License, Version 2.0 (the "License");
    6   // you may not use this file except in compliance with the License.
    7   // You may obtain a copy of the License at
    8   // http://www.apache.org/licenses/LICENSE-2.0
    9   // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  // ========================================================================
    15
    16  package org.mortbay.jetty.security;
    17
    18  import java.io.IOException;
    19  import java.security.Principal;
    20  import java.sql.Connection;
    21  import java.sql.DriverManager;
    22  import java.sql.PreparedStatement;
    23  import java.sql.ResultSet;
    24  import java.sql.SQLException;
    25  import java.util.Properties;
    26
    27  import org.mortbay.jetty.Request;
    28  import org.mortbay.log.Log;
    29  import org.mortbay.resource.Resource;
    30  import org.mortbay.util.Loader;
    31
    32  /* ------------------------------------------------------------ */
    33  /** HashMapped User Realm with JDBC as data source.
    34   * JDBCUserRealm extends HashUserRealm and adds a method to fetch user
    35   * information from database.
    36   * The authenticate() method checks the inherited HashMap for the user.
    37   * If the user is not found, it will fetch details from the database
    38   * and populate the inherited HashMap. It then calls the HashUserRealm
    39   * authenticate() method to perform the actual authentication.
    40   * Periodically (controlled by configuration parameter), internal
    41   * hashes are cleared. Caching can be disabled by setting cache
    42   * refresh interval to zero.
    43   * Uses one database connection that is initialized at startup. Reconnect
    44   * on failures. authenticate() is 'synchronized'.
    45   *
    46   * An example properties file for configuration is in
    47   * $JETTY_HOME/etc/jdbcRealm.properties
    48   *
    49   * @version $Id: JDBCUserRealm.java 3345 2008-07-22 05:58:07Z dyu $
    50   * @author Arkadi Shishlov (arkadi)
    51   * @author Fredrik Borgh
    52   * @author Greg Wilkins (gregw)
    53   * @author Ben Alex
    54   */
    55
    56  public class JDBCUserRealm extends HashUserRealm implements UserRealm
    57  {
    58
    59      private String _jdbcDriver;
    60      private String _url;
    61      private String _userName;
    62      private String _password;
    63      private String _userTable;
    64      private String _userTableKey;
    65      private String _userTableUserField;
    66      private String _userTablePasswordField;
    67      private String _roleTable;
    68      private String _roleTableKey;
    69      private String _roleTableRoleField;
    70      private String _userRoleTable;
    71      private String _userRoleTableUserKey;
    72      private String _userRoleTableRoleKey;
    73      private int _cacheTime;
    74
    75      private long _lastHashPurge;
    76      private Connection _con;
    77      private String _userSql;
    78      private String _roleSql;
    79
    80      /* ------------------------------------------------------------ */
    81      /** Constructor.
    82       */
    83      public JDBCUserRealm()
    84      {
    85          super();
    86      }
    87
    88      /* ------------------------------------------------------------ */
    89      /** Constructor.
    90       * @param name
    91       */
    92      public JDBCUserRealm(String name)
    93      {
    94          super(name);
    95      }
    96
    97      /* ------------------------------------------------------------ */
    98      /** Constructor.
    99       * @param name Realm name
    100      * @param config Filename or url of JDBC connection properties file.
    101      * @exception IOException
    102      * @exception ClassNotFoundException
    103      */
    104     public JDBCUserRealm(String name, String config)
    105         throws IOException,
    106                ClassNotFoundException,
    107                InstantiationException,
    108                IllegalAccessException
    109     {
    110         super(name);
    111         setConfig(config);
    112         Loader.loadClass(this.getClass(),_jdbcDriver).newInstance();
    113         connectDatabase();
    114     }
    115
    116     /* ------------------------------------------------------------ */
    117     /** Load JDBC connection configuration from properties file.
    118      *
    119      * @exception IOException
    120      */
    121     protected void loadConfig()
    122         throws IOException
    123     {
    124         Properties properties = new Properties();
    125
    126         properties.load(getConfigResource().getInputStream());
    127
    128         _jdbcDriver = properties.getProperty("jdbcdriver");
    129         _url = properties.getProperty("url");
    130         _userName = properties.getProperty("username");
    131         _password = properties.getProperty("password");
    132         _userTable = properties.getProperty("usertable");
    133         _userTableKey = properties.getProperty("usertablekey");
    134         _userTableUserField = properties.getProperty("usertableuserfield");
    135         _userTablePasswordField = properties.getProperty("usertablepasswordfield");
    136         _roleTable = properties.getProperty("roletable");
    137         _roleTableKey = properties.getProperty("roletablekey");
    138         _roleTableRoleField = properties.getProperty("roletablerolefield");
    139         _userRoleTable = properties.getProperty("userroletable");
    140         _userRoleTableUserKey = properties.getProperty("userroletableuserkey");
    141         _userRoleTableRoleKey = properties.getProperty("userroletablerolekey");
    142         // default cachetime = 30s
    143         String cachetime = properties.getProperty("cachetime");
    144         _cacheTime = cachetime!=null ? new Integer(cachetime).intValue() : 30;
    145
    146         if (_jdbcDriver == null || _jdbcDriver.equals("")
    147             || _url == null || _url.equals("")
    148             || _userName == null || _userName.equals("")
    149             || _password == null
    150             || _cacheTime < 0)
    151         {
    152             if(Log.isDebugEnabled())Log.debug("UserRealm " + getName()
    153                         + " has not been properly configured");
    154         }
    155         _cacheTime *= 1000;
    156         _lastHashPurge = 0;
    157         _userSql = "select " + _userTableKey + ","
    158             + _userTablePasswordField + " from "
    159             + _userTable + " where "
    160             + _userTableUserField + " = ?";
    161         _roleSql = "select r." + _roleTableRoleField
    162             + " from " + _roleTable + " r, "
    163             + _userRoleTable + " u where u."
    164             + _userRoleTableUserKey + " = ?"
    165             + " and r." + _roleTableKey + " = u."
    166             + _userRoleTableRoleKey;
    167     }
    168
    169     /* ------------------------------------------------------------ */
    170     public void logout(Principal user)
    171     {}
    172
    173     /* ------------------------------------------------------------ */
    174     /** (re)Connect to database with parameters setup by loadConfig()
    175      */
    176     public void connectDatabase()
    177     {
    178         try
    179         {
    180              Class.forName(_jdbcDriver);
    181             _con = DriverManager.getConnection(_url, _userName, _password);
    182         }
    183         catch(SQLException e)
    184         {
    185             Log.warn("UserRealm " + getName()
    186                       + " could not connect to database; will try later", e);
    187         }
    188         catch(ClassNotFoundException e)
    189         {
    190             Log.warn("UserRealm " + getName()
    191                       + " could not connect to database; will try later", e);
    192         }
    193     }
    194
    195     /* ------------------------------------------------------------ */
    196     public Principal authenticate(String username,
    197                                   Object credentials,
    198                                   Request request)
    199     {
    200         synchronized (this)
    201         {
    202             long now = System.currentTimeMillis();
    203             if (now - _lastHashPurge > _cacheTime || _cacheTime == 0)
    204             {
    205                 _users.clear();
    206                 _roles.clear();
    207                 _lastHashPurge = now;
    208             }
    209             Principal user = super.getPrincipal(username);
    210             if (user == null)
    211             {
    212                 loadUser(username);
    213                 user = super.getPrincipal(username);
    214             }
    215         }
    216         return super.authenticate(username, credentials, request);
    217     }
    218
    219     /* ------------------------------------------------------------ */
    220     /** Check if a user is in a role.
    221      * @param user The user, which must be from this realm
    222      * @param roleName
    223      * @return True if the user can act in the role.
    224      */
    225     public synchronized boolean isUserInRole(Principal user, String roleName)
    226     {
    227         if(super.getPrincipal(user.getName())==null)
    228             loadUser(user.getName());
    229         return super.isUserInRole(user, roleName);
    230     }
    231
    232
    233
    234
    235     /* ------------------------------------------------------------ */
    236     private void loadUser(String username)
    237     {
    238         try
    239         {
    240             if (null==_con)
    241                 connectDatabase();
    242
    243             if (null==_con)
    244                 throw new SQLException("Can't connect to database");
    245
    246             PreparedStatement stat = _con.prepareStatement(_userSql);
    247             stat.setObject(1, username);
    248             ResultSet rs = stat.executeQuery();
    249
    250             if (rs.next())
    251             {
    252                 int key = rs.getInt(_userTableKey);
    253                 put(username, rs.getString(_userTablePasswordField));
    254                 stat.close();
    255
    256                 stat = _con.prepareStatement(_roleSql);
    257                 stat.setInt(1, key);
    258                 rs = stat.executeQuery();
    259
    260                 while (rs.next())
    261                     addUserToRole(username, rs.getString(_roleTableRoleField));
    262
    263                 stat.close();
    264             }
    265         }
    266         catch (SQLException e)
    267         {
    268             Log.warn("UserRealm " + getName()
    269                       + " could not load user information from database", e);
    270             connectDatabase();
    271         }
    272     }
    273 }