@Override
  public void onAuthentication(
      Authentication authentication, HttpServletRequest request, HttpServletResponse response)
      throws SessionAuthenticationException {

    ClusterSecurityContext context =
        (ClusterSecurityContext)
            securityContextRepository.loadContext(new HttpRequestResponseHolder(request, response));
    context.setAuthentication(authentication);

    // filter total max sessions first
    if (maxSessions > 0) {
      int postAuthSessionCount = 1; // the login
      Collection<String> sessionIds =
          securityContextRepository.getClusterSessionIdsForPrinciple(authentication);
      postAuthSessionCount += sessionIds.size();

      if (postAuthSessionCount > maxSessions) {
        purgeSessions(request, sessionIds, postAuthSessionCount - maxSessions);
      }
    }

    // filter client type sessions
    if (maxSessionsByClientType != null) {
      Collection<ClientType> clientTypes = clientTypeResolver.resolveClientType(request);
      if (clientTypes != null) {
        int maxClientTypeSessions = 0;
        int postAuthSessionCount = 0;
        Collection<String> sessionIds = null;
        for (ClientType type : clientTypes) {
          if (maxSessionsByClientType.containsKey(type)) {
            postAuthSessionCount = 1; // the login
            maxClientTypeSessions = maxSessionsByClientType.get(type);
            sessionIds =
                securityContextRepository.getClusterSessionIdsForPrinciple(authentication, type);
            postAuthSessionCount += sessionIds.size();

            if (postAuthSessionCount > maxClientTypeSessions) {
              purgeSessions(request, sessionIds, postAuthSessionCount - maxClientTypeSessions);
            }
          }
        }
      }
    }

    // session fixation
    if ((context.getClusterSessionId() == null) || sessionFixation) {
      if (context.getClusterSessionId() != null) {
        securityContextRepository.expire(request, context.getClusterSessionId());
      }
      context.setClusterSessionId(clusterSessionIdGenerator.generate(request));
      clusterSessionIdResolver.sendClusterSessionId(
          request, response, context.getClusterSessionId());
    }
    context.setAuthTimestamp(System.currentTimeMillis());
    context.setLastUpdateTimestamp(System.currentTimeMillis());
    securityContextRepository.saveContext(context, request, response);
  }
  private void purgeSessions(
      HttpServletRequest request, Collection<String> sessionIds, int sessionCount) {

    Collection<ClusterSecurityContext> contexts = new ArrayList<ClusterSecurityContext>();
    boolean lastUpdatesComplete = true;

    ClusterSecurityContext context = null;
    for (String id : sessionIds) {
      context = securityContextRepository.loadById(id);
      if (context != null) {
        if (context.getLastUpdateTimestamp() == 0) {
          lastUpdatesComplete = false;
        }
        contexts.add(context);
      }
    }

    Map<Long, String> sortedSessions = new TreeMap<Long, String>();

    for (ClusterSecurityContext ctx : contexts) {
      if (lastUpdatesComplete) {
        sortedSessions.put(ctx.getLastUpdateTimestamp(), ctx.getClusterSessionId());
      } else {
        sortedSessions.put(ctx.getAuthTimestamp(), ctx.getClusterSessionId());
      }
    }

    Iterator<String> itr = sortedSessions.values().iterator();

    int purgedCount = 0;
    while (itr.hasNext() && (purgedCount < sessionCount)) {
      if (maxSessionsByClientType != null) {
        securityContextRepository.expire(request, itr.next(), maxSessionsByClientType.keySet());
      } else {
        securityContextRepository.expire(request, itr.next());
      }
      purgedCount++;
    }
  }