View Javadoc

    1   /* Copyright 2004, 2005 Acegi Technology Pty Limited
    2    *
    3    * Licensed under the Apache License, Version 2.0 (the "License");
    4    * you may not use this file except in compliance with the License.
    5    * You may obtain a copy of the License at
    6    *
    7    *     http://www.apache.org/licenses/LICENSE-2.0
    8    *
    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.acegisecurity.concurrent;
    17
    18  import org.acegisecurity.AcegiMessageSource;
    19  import org.acegisecurity.Authentication;
    20  import org.acegisecurity.AuthenticationException;
    21  import org.springframework.beans.factory.InitializingBean;
    22  import org.springframework.context.MessageSource;
    23  import org.springframework.context.MessageSourceAware;
    24  import org.springframework.context.support.MessageSourceAccessor;
    25  import org.springframework.util.Assert;
    26
    27
    28  /***
    29   * Base implementation of {@link ConcurrentSessionControllerImpl} which
    30   * prohibits simultaneous logins.
    31   *
    32   * <p>
    33   * By default uses {@link org.acegisecurity.concurrent.SessionRegistryImpl},
    34   * although any <code>SessionRegistry</code> may be used.
    35   * </p>
    36   */
    37  public class ConcurrentSessionControllerImpl
    38      implements ConcurrentSessionController, InitializingBean,
    39          MessageSourceAware {
    40      //~ Instance fields ========================================================
    41
    42      protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
    43      private SessionRegistry sessionRegistry = new SessionRegistryImpl();
    44      private boolean exceptionIfMaximumExceeded = false;
    45      private int maximumSessions = 1;
    46
    47      //~ Methods ================================================================
    48
    49      public void afterPropertiesSet() throws Exception {
    50          Assert.notNull(sessionRegistry, "SessionRegistry required");
    51          Assert.isTrue(maximumSessions != 0,
    52              "MaximumLogins must be either -1 to allow unlimited logins, or a positive integer to specify a maximum");
    53          Assert.notNull(this.messages, "A message source must be set");
    54      }
    55
    56      /***
    57       * Allows subclasses to customise behaviour when too many sessions are
    58       * detected.
    59       *
    60       * @param sessionId the session ID of the present request
    61       * @param sessions either <code>null</code> or all unexpired sessions
    62       *        associated with the principal
    63       * @param allowableSessions DOCUMENT ME!
    64       * @param registry an instance of the <code>SessionRegistry</code> for
    65       *        subclass use
    66       *
    67       * @throws ConcurrentLoginException DOCUMENT ME!
    68       */
    69      protected void allowableSessionsExceeded(String sessionId,
    70          SessionInformation[] sessions, int allowableSessions,
    71          SessionRegistry registry) {
    72          if (exceptionIfMaximumExceeded || (sessions == null)) {
    73              throw new ConcurrentLoginException(messages.getMessage(
    74                      "ConcurrentSessionControllerImpl.exceededAllowed",
    75                      new Object[] {new Integer(allowableSessions)},
    76                      "Maximum sessions of {0} for this principal exceeded"));
    77          }
    78
    79          // Determine least recently used session, and mark it for invalidation
    80          SessionInformation leastRecentlyUsed = null;
    81
    82          for (int i = 0; i < sessions.length; i++) {
    83              if ((leastRecentlyUsed == null)
    84                  || sessions[i].getLastRequest()
    85                                .before(leastRecentlyUsed.getLastRequest())) {
    86                  leastRecentlyUsed = sessions[i];
    87              }
    88          }
    89
    90          leastRecentlyUsed.expireNow();
    91      }
    92
    93      public void checkAuthenticationAllowed(Authentication request)
    94          throws AuthenticationException {
    95          Assert.notNull(request,
    96              "Authentication request cannot be null (violation of interface contract)");
    97
    98          Object principal = SessionRegistryUtils
    99                  .obtainPrincipalFromAuthentication(request);
    100             String sessionId = SessionRegistryUtils
    101                     .obtainSessionIdFromAuthentication(request);
    102
    103                 SessionInformation[] sessions = sessionRegistry.getAllSessions(principal);
    104
    105                 int sessionCount = 0;
    106
    107                 if (sessions != null) {
    108                     sessionCount = sessions.length;
    109                 }
    110
    111                 int allowableSessions = getMaximumSessionsForThisUser(request);
    112                 Assert.isTrue(allowableSessions != 0,
    113                     "getMaximumSessionsForThisUser() must return either -1 to allow unlimited logins, or a positive integer to specify a maximum");
    114
    115                 if (sessionCount < allowableSessions) {
    116                     return;
    117                 } else if (sessionCount == allowableSessions) {
    118                     // Only permit it though if this request is associated with one of the sessions
    119                     for (int i = 0; i < sessionCount; i++) {
    120                         if (sessions[i].getSessionId().equals(sessionId)) {
    121                             return;
    122                         }
    123                     }
    124                 }
    125
    126                 allowableSessionsExceeded(sessionId, sessions,
    127                     allowableSessions, sessionRegistry);
    128             }
    129
    130             /***
    131              * Method intended for use by subclasses to override the maximum
    132              * number of sessions that are permitted for a particular
    133              * authentication. The default implementation simply returns the
    134              * <code>maximumSessions</code> value for the bean.
    135              *
    136              * @param authentication to determine the maximum sessions for
    137              *
    138              * @return either -1 meaning unlimited, or a positive integer to
    139              *         limit (never zero)
    140              */
    141             protected int getMaximumSessionsForThisUser(
    142                 Authentication authentication) {
    143                 return maximumSessions;
    144             }
    145
    146             public void registerSuccessfulAuthentication(
    147                 Authentication authentication) {
    148                 Assert.notNull(authentication,
    149                     "Authentication cannot be null (violation of interface contract)");
    150
    151                 Object principal = SessionRegistryUtils
    152                         .obtainPrincipalFromAuthentication(authentication);
    153                     String sessionId = SessionRegistryUtils
    154                             .obtainSessionIdFromAuthentication(authentication);
    155
    156                         sessionRegistry.removeSessionInformation(sessionId);
    157                         sessionRegistry.registerNewSession(sessionId, principal);
    158                     }
    159
    160                     public void setExceptionIfMaximumExceeded(
    161                         boolean exceptionIfMaximumExceeded) {
    162                         this.exceptionIfMaximumExceeded = exceptionIfMaximumExceeded;
    163                     }
    164
    165                     public void setMaximumSessions(int maximumSessions) {
    166                         this.maximumSessions = maximumSessions;
    167                     }
    168
    169                     public void setMessageSource(MessageSource messageSource) {
    170                         this.messages = new MessageSourceAccessor(messageSource);
    171                     }
    172
    173                     public void setSessionRegistry(
    174                         SessionRegistry sessionRegistry) {
    175                         this.sessionRegistry = sessionRegistry;
    176                     }
    177                 }