springboot相同IP限制接口访问次数(AOP事物)

  • Post author:
  • Post category:其他


应用场景:应项目需求,前后台分离,增强接口安全,限制相同IP访问接口的次数有限

解决:在项目中增加事物,在进接口方法钱处理

思路:

1)@Before 在进入接口方法之前进入处理,within里面的书写表示写在控制器上方并且有对应注解的控制器会来到这里。

2)用获取的IP+对应url作为key,存到redis中,如果Redis存在这个key,然后时间处理器在超过设置时间时,会将相应的key移除。

3)如果访问超出设置限定的次数,就会重定向到返回数据接口。返回信息为请求次数过多。

1、增加Aspects的依赖


<!– https://mvnrepository.com/artifact/org.springframework/spring-aspects –>


<dependency>


<groupId>

org.springframework

</groupId>


<artifactId>

spring-aspects

</artifactId>


</dependency>

2.定义自己的注解

package com.haidaipuhui.safeLimit;

import java.lang.annotation.*;

import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;

/**
 * @author wangdeqiu
 * @date 2018年11月2日 下午2:19:05
 * 类说明:自定义注解限制访问时间长度最多访问次数
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface RequestLimit {

	/**
     * 允许访问的最大次数
     */
    int count() default Integer.MAX_VALUE;

    /**
     * 时间段,单位为毫秒,默认值一分钟
     */
    long time() default 60000;
}

3.定义自己的异常类

package com.haidaipuhui.safeLimit;
/**
 * @author wangdeqiu
 * @date 2018年11月2日 下午2:22:28
 * 类说明:
 */
public class RequestLimitException extends Exception{

	private static final long serialVersionUID = 1555967171104727461L;
	
	public RequestLimitException(){
        super("HTTP请求超出设定的限制");
    }

    public RequestLimitException(String message){
        super(message);
    }

}

4.定义切点,处理逻辑

package com.haidaipuhui.safeLimit;

import java.util.Timer;
import java.util.TimerTask;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.github.pagehelper.util.StringUtil;
import com.haidaipuhui.service.redis.IRedisService;

/**
 * @author wangdeqiu
 * @date 2018年11月2日 下午2:22:43
 * 类说明:
 */
@Aspect
@Component
public class RequestLimitContract {

	private static final Logger logger = LoggerFactory.getLogger(RequestLimitContract.class);
    @Autowired 
    private IRedisService redisTemplate;
    private static String limitPath="/safeLimit/limit";
	
    @Before("within(@org.springframework.stereotype.Controller *) && @annotation(limit)")
    public void requestLimit(final JoinPoint joinPoint , RequestLimit limit) throws RequestLimitException {
        try {
            Object[] args = joinPoint.getArgs();
            HttpServletRequest request = null;
            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
            HttpServletResponse response = servletRequestAttributes.getResponse();
            for (int i = 0; i < args.length; i++) {
                if (args[i] instanceof HttpServletRequest) {
                    request = (HttpServletRequest) args[i];
                    break;
                }
            }
            if (request == null) {
                throw new RequestLimitException("方法中缺失HttpServletRequest参数");
            }
            String ip = request.getLocalAddr();
            String url = request.getRequestURL().toString();
            String key = "req_limit_".concat(url).concat(ip);
            if (!redisTemplate.hasKey(key) || StringUtil.isEmpty(redisTemplate.get(key))) {
                redisTemplate.set(key,String.valueOf(1));
            } else {
            		Integer getValue = Integer.parseInt(redisTemplate.get(key))+1;
                redisTemplate.set(key, String.valueOf(getValue));
            }
            int count =Integer.parseInt(redisTemplate.get(key));
            if (count > 0) {
                //创建一个定时器
                Timer timer = new Timer();
                TimerTask timerTask = new TimerTask() {
                    @Override
                    public void run() {
                        redisTemplate.del(key);
                    }
                };
                //这个定时器设定在time规定的时间之后会执行上面的remove方法,也就是说在这个时间后它可以重新访问
                timer.schedule(timerTask, limit.time());
            }
            if (count > limit.count()) {
                logger.info("用户IP[" + ip + "]访问地址[" + url + "]超过了限定的次数[" + limit.count() + "]");
//                throw new RequestLimitException();
                String toLomitPath ="http://" + request.getServerName()+ ":" + request.getServerPort()+limitPath;   //端口号
                response.sendRedirect(toLomitPath);
            }
        }catch (RequestLimitException e){
            throw e;
        }catch (Exception e){
            logger.error("发生异常",e);
        }
    }

    
}

5。返回给前端信息

package com.haidaipuhui.safeLimit;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @author wangdeqiu
 * @date 2018年11月5日 上午11:37:45
 * 类说明:
 */
@Controller
@RequestMapping("safeLimit")
public class RequestLimitExpController {
	
	
	@RequestMapping(value = "/limit")
    @ResponseBody
    public Map<String, Object> safeLimitRes(HttpServletRequest request,Integer pagenum, Integer pagesize){
    		Map<String, Object> outputMap =new HashMap<String,Object>();  
    		outputMap.put("success", false);
    		outputMap.put("msg", "HTTP请求超出设定的限制");
        	return outputMap;
    }  

}



版权声明:本文为befroe_You原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。