通过Request限制访问网站请求并发量

一、场景
针对开放在公网上的网站可能会被攻击,必须一定的安全措施,需要对并发进行限制,如限制网站的并发量或是单个IP短时间的登录次数、token认证等。

二、代码实现
以下是通过监控、记录网站的request请求数量,实现并发限制。
1.在配置文件中配置最大同时请求数

#####最大同时请求数
visitor.order.online.count=100

2.首先实现ServletRequestListener接口,在Request创建和销毁时进行记数,并将数量记录在ServletContext全局对象中。

@Slf4j
public class RequestListener implements ServletRequestListener {

	private static Logger logger = LoggerFactory.getLogger(RequestListener.class);

	public void requestInitialized(ServletRequestEvent arg0) {
		// TODO Auto-generated method stub
		synchronized (logger) {
			if(logger.isInfoEnabled()){
				logger.info(RunTimeLogUtil.toLog(LogObjectEnum.SERVICE, "RequestListener_requestInitialized","start", null, null));
			}
			//获得当前在线人数,并将其加一
			Integer onlineNum = (Integer) arg0.getServletContext().getAttribute("onlineNum");
			arg0.getServletContext().setAttribute("onlineNum", ++onlineNum);

			if (logger.isInfoEnabled()) {
				logger.info(RunTimeLogUtil.toLog(LogObjectEnum.SERVICE, "RequestListener_requestInitialized", "RequestListener_end", null, null, "onlineNum"), onlineNum);
			}
		}
	}

	public void requestDestroyed(ServletRequestEvent arg0) {
		// TODO Auto-generated method stub
		synchronized (logger) {
			if(logger.isInfoEnabled()){
				logger.info(RunTimeLogUtil.toLog(LogObjectEnum.SERVICE, "RequestListener_requestDestroyed","start", null, null));
			}
			//销毁请求的时候,需要将在线人数减一
			Integer onlineNum = (Integer) arg0.getServletContext().getAttribute("onlineNum");
			arg0.getServletContext().setAttribute("onlineNum", --onlineNum);

			if(logger.isInfoEnabled()) {
				logger.info(RunTimeLogUtil.toLog(LogObjectEnum.SERVICE, "RequestListener_requestDestroyed","RequestListener_end", null, null, "onlineNum"), onlineNum);
			}
		}
	}
}

3.创建拦截器,拦截过滤所有请求,check当前请求数量是否达到配置的上限,若没有放行,否则拦截并返回错误信息。
注意加锁

public class OnLineUserInterceptor extends HandlerInterceptorAdapter{

    private static Logger logger = LoggerFactory.getLogger(OnLineUserInterceptor.class);

    @Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
        if(logger.isInfoEnabled()){
            logger.info(RunTimeLogUtil.toLog(LogObjectEnum.SERVICE, "OnLineUserInterceptor","start", null, null));
        }
		synchronized (logger) {
            //从缓存中获取当前连接数
            Integer onlineNum = (Integer) request.getServletContext().getAttribute("onlineNum");
            // 现获取请求数量
            String onlineMaxCount = PropertiesReader.getWebProperty(RvsConstant.VISITOR_ORDER_ONLINE_COUNT);
            // 如果未配置当前请求数量最大值,则放行
            if (VisitorStringUtils.isEmpty(onlineMaxCount)) {
                return super.preHandle(request, response, handler);
            }

			if (onlineNum > Integer.parseInt(onlineMaxCount)) {

                // 如果获取当前请求数量大于最大值,不允许使用
                ActionResult actionResult = new ActionResult(
                        VisitorStringUtils.toHexString(ErrorCodeConstant.RESOURCE_NUM_EXCEED_LIMIT),
                        MsgKeyConstant.RESOURCE_NUM_EXCEED_LIMIT);
                logger.error(Log.toLog(StringOrNumericUtils.toHexString(ErrorCodeConstant.OPERATE_FAIL),
                        "Please try later again ! "));
                response.getWriter().write(JsonUtils.object2Json(actionResult));
                return false;
			}
		}
        if(logger.isInfoEnabled()){
            logger.info(RunTimeLogUtil.toLog(LogObjectEnum.SERVICE, "OnLineUserInterceptor","end", null, null));
        }
		return super.preHandle(request, response, handler);
	}
}

4.在springmvc中配置该拦截器

<!-- 拦截器 配置多个将会顺序执行 -->
<mvc:interceptors>
    <!-- 当前request数量校验 -->
    <bean class="com.controller.common.filter.OnLineUserInterceptor"/>
</mvc:interceptors>

你可能感兴趣的:(代码功能框架)