springboot定义注解防止接口被恶意请求多次

实现原理:

利用spring拦截器来实现,定义注解,在需要的方法上加上该注解,通过拦截器拦截这些注解的方法后,进行接口存储到redis中,当用户多次请求时,我们可以累积他的请求次数,达到了上限,我们就可以给他提示信息。

实现方法:

1.定义注解

package com.myzzb.mall.core.bean;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * @author :zzb
 * @createDate :2020/7/21 16:17
 * @desc :定义接口恶意请求多次注解
 */
@Retention(RUNTIME)//表示它在运行时
@Target(METHOD) //表示它只能放在方法上
@SuppressWarnings("all")
public @interface AccessLimit {

    int seconds();//规定几秒
    int maxCount();//最大请求数
    boolean needLogin()default true;//是否需要登录
}

2.配置拦截器

package com.myzzb.mall.restful.bean;
import com.alibaba.fastjson.JSONObject;
import com.myzzb.mall.core.bean.AccessLimit;

import com.myzzb.mall.core.utils.RedisUtil;
import com.myzzb.mall.core.utils.ResultBean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

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

/**
 * @author :zzb
 * @createDate :2020/7/18 6:54 下午
 * @desc : 接口拦截器
 */
@Component
@SuppressWarnings("all")
public class APIInterceptor extends HandlerInterceptorAdapter {

    private final Log log = LogFactory.getLog(APIInterceptor.class);

    @Resource
    private RedisUtil.redisString redisString;

    /**
     * 预处理回调方法,实现处理器的预处理(如登陆检查/判断同一对象短时间内是否重复调用接口等) 第三个参数为相应的处理器即controller
     * f返回true表示流程继续,调用下一个拦截器或者处理器,返回false表示流程中断,通过response产生响应
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        //判断同一用户短时间内是否重复请求接口
        log.info("========================request path==============================>"+ request.getRequestURI());
        //判断请求是否属于方法的请求
        if(handler instanceof HandlerMethod){
            HandlerMethod hm = (HandlerMethod) handler;
            //获取方法中的注解,看是否有该注解
            AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
            log.info("========================accessLimit==============================>"+ accessLimit);
            if(accessLimit == null){
                return true;
            }
            int seconds = accessLimit.seconds();
            int maxCount = accessLimit.maxCount();
            boolean login = accessLimit.needLogin();
            //如果需要登录
            if(login){
                //获取登录的session进行判断
                log.info("------------------------需要登录 ----------->"+request.getRemoteAddr());
            }

            String ip=request.getRemoteAddr();
            String key = request.getServletPath() + ":" + ip ;
            Integer count = (Integer) redisString.get(key);
            if (null == count || -1 == count) {
                redisString.set(key, 1,seconds);
                return true;
            }

            if (count < maxCount) {
                count = count+1;
                redisString.set(key, count,seconds);
                return true;
            }

            if (count >= maxCount) {
                //response 返回 json 请求过于频繁请稍后再试
                ResultBean resultBean = new ResultBean(9999,"操作过于频繁");
                response.setCharacterEncoding("UTF-8");
                response.setContentType("application/json; charset=utf-8");
                Object obj = JSONObject.toJSON(resultBean);
                response.getWriter().write(JSONObject.toJSONString(obj));
                return false;
            }
    }

        return super.preHandle(request, response, handler);
    }


    /**
     * 当请求进行处理之后,也就是controller方法调用之后执行,但是他会在DispatcherServlet进行视图渲染之前被调用
     * 此时我们可以通过modelAndView对模型数据进行处理或对视图进行处理
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {

    }

    /**
     * 方法将在整个请求结束之后,也就是DispatcheServlet进行视图渲染之后执行,这个方法的主要作用是对资源的清理工作
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {

    }

}

3.注入拦截器

package com.myzzb.mall.restful.bean;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author :zzb
 * @createDate :2020/7/18 19:20
 * @desc :
 */
@SuppressWarnings("all")
@Configuration
public class WebConfig implements WebMvcConfigurer {


    @Autowired
    private APIInterceptor apiInterceptor;

    // 这个方法是用来配置静态资源的,比如html,js,css,等等
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
    }

    // 这个方法用来注册拦截器,我们自己写好的拦截器需要通过这里添加注册才能生效
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 多个拦截器组成一个拦截器链
        // addPathPatterns 用于添加拦截规则
        // excludePathPatterns 用于排除拦截路径
        registry.addInterceptor(apiInterceptor).addPathPatterns("/**").excludePathPatterns("/test/*");     }

}

4.在接口方上引用注解

package com.myzzb.mall.restful.order;

import com.alibaba.dubbo.config.annotation.Reference;
import com.myzzb.mall.core.bean.AccessLimit;
import com.myzzb.mall.core.exception.BaseException;
import com.myzzb.mall.core.utils.ResultBean;
import com.myzzb.mall.facade.order.IOrderService;
import com.myzzb.mall.restful.bean.SystemUtil;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

/**
 * @author :zzb
 * @createDate :2020/7/25 19:08
 * @desc : 订单模块
 */
@RestController
@RequestMapping("/order")
public class OrderAPI {

    @Reference(version = "1.0.0")
    private IOrderService orderService;

    @Resource
    private SystemUtil systemUtil;

    @RequestMapping("/add")
    @AccessLimit(seconds = 5, maxCount = 3 , needLogin = true) //5秒内 允许请求3次
    public ResultBean addOrder(HttpServletRequest request){
        return orderService.addOrder(systemUtil.getRequestParams(request));
    }

}

总结:

采用注解方式加拦截器,结合redis来存储请求次数,可以灵活配置请求的接口是否需要登录,同一用户在规定的时间内请求同一接口最大次数。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值