/* * Licensed to Jasig under one or more contributor license * agreements. See the NOTICE file distributed with this work * for additional information regarding copyright ownership. * Jasig licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a * copy of the License at the following location: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.jasig.cas.web.support; import java.util.Date; import java.util.Iterator; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import javax.servlet.http.HttpServletRequest; /** * Implementation of a HandlerInterceptorAdapter that keeps track of a mapping * of IP Addresses to number of failures to authenticate. * <p> * Note, this class relies on an external method for decrementing the counts * (i.e. a Quartz Job) and runs independent of the threshold of the parent. * * @author Scott Battaglia * @since 3.0.5 */ public abstract class AbstractInMemoryThrottledSubmissionHandlerInterceptorAdapter extends AbstractThrottledSubmissionHandlerInterceptorAdapter { private final ConcurrentMap<String, Date> ipMap = new ConcurrentHashMap<String, Date>(); @Override protected final boolean exceedsThreshold(final HttpServletRequest request) { final Date last = this.ipMap.get(constructKey(request)); if (last == null) { return false; } return submissionRate(new Date(), last) > getThresholdRate(); } @Override protected final void recordSubmissionFailure(final HttpServletRequest request) { this.ipMap.put(constructKey(request), new Date()); } protected abstract String constructKey(HttpServletRequest request); /** * This class relies on an external configuration to clean it up. It ignores the threshold data in the parent class. */ public final void decrementCounts() { final Set<String> keys = this.ipMap.keySet(); logger.debug("Decrementing counts for throttler. Starting key count: {}", keys.size()); final Date now = new Date(); String key; for (final Iterator<String> iter = keys.iterator(); iter.hasNext();) { key = iter.next(); if (submissionRate(now, this.ipMap.get(key)) < getThresholdRate()) { logger.trace("Removing entry for key {}", key); iter.remove(); } } logger.debug("Done decrementing count for throttler."); } /** * Computes the instantaneous rate in between two given dates corresponding to two submissions. * * @param a First date. * @param b Second date. * * @return Instantaneous submission rate in submissions/sec, e.g. <code>a - b</code>. */ private double submissionRate(final Date a, final Date b) { return 1000.0 / (a.getTime() - b.getTime()); } }