如何防止接口被恶意请求?添加时间戳检验?

添加时间戳校验位

比如给客户端提供一个timestamp参数,值是13位的毫秒级时间戳,可以在第12位或者13位做一个校验位,通过一定的算法给其他12位的值做一个校验。

举例:现在实际时间是 1684059940123,我把前12位通过算法算出来校验位的值是9,参数则把最后一位改成9,即1684059940129,值传到服务端后通过前十二位也可以算出来值,来判断这个时间戳是不是合法的。

生成校验位的算法要确保前后端一致,比如校验位的值都取倒数第二位的值(可过滤掉90%)恶意请求。

添加拦截器防止接口恶意狂刷

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

添加注解

@Target({ElementType.METHOD, ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface AccessLimit {

    int seconds();

    int maxCount();

}

自定义拦截器

public class SessionInterceptor extends HandlerInterceptorAdapter {

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

    @Autowired

    private UserAuthService userAuthService;

    @Autowired

    private UserContext userContext;

 @Autowired

 private RedisUtil redisUtil;

    @Override

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String session = request.getHeader("Authorization");

        if (StringUtils.isEmpty(session)) {

            throw new UserException(ReturnCode.PARAMETERS_ERROR, "缺少session");

        }

        HandlerMethod hm = (HandlerMethod) handler;

        //获取方法中的注解,看是否有该注解

        AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);

        if(accessLimit != null){

         int seconds = accessLimit.seconds();

         int maxCount = accessLimit.maxCount();       

         //从redis中获取用户访问的次数

         String ip = request.getHeader("x-forwarded-for"); // 有可能ip是代理的

            if(ip ==null || ip.length() ==0 || "unknown".equalsIgnoreCase(ip)) {      

                ip = request.getHeader("Proxy-Client-IP");      

            }      

            if(ip ==null || ip.length() ==0 || "unknown".equalsIgnoreCase(ip)) {      

                ip = request.getHeader("WL-Proxy-Client-IP");      

            }      

            if(ip ==null || ip.length() ==0 || "unknown".equalsIgnoreCase(ip)) {      

                 ip = request.getRemoteAddr();      

            } 

         Integer count = (Integer)redisUtil.get(RedisKey.SECOND_ACCESS + ip);

         if(count == null){

          //第一次访问

        redisUtil.set(RedisKey.SECOND_ACCESS + ip, 1, seconds);

         }else if(count < maxCount){

          //加1

          count = count + 1;

       redisUtil.incr(RedisKey.SECOND_ACCESS + ip, count);

         }else{

          //超出访问次数

                logger.info("访问过快ip ===> " + ip + " 且在 " + seconds + " 秒内超过最大限制 ===> " + maxCount + " 次数达到 ====> " + count);

          response.setCharacterEncoding("UTF-8");          response.setContentType("application/json; charset=utf-8");

          ReturnObject result = new ReturnObject();         result.setCode(ReturnCode.OPERATION_FAILED.getCode());

          result.setData("操作太快了");

          Object obj = JSONObject.toJSON(result);         response.getWriter().write(JSONObject.toJSONString(obj));

          return false;

         }

        }       

        Integer userId = userAuthService.getUserIdBySession(session);

        //获取登录的session进行判断

        if (userId == null || userId == 0) {

            throw new UserException(ReturnCode.BAD_REQUEST, "无效session");

        }       

        userContext.setUserId(userId);

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

    }

}

 

添加拦截器

@Configuration

public class WebConfig extends WebMvcConfigurationSupport { 

    @Autowired

    private SessionInterceptor interceptor;

    @Override

    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(interceptor);

    }

}

这里采用了注解方式(拦截器),结合redis来存储请求次数,达到上限就不让用户操作。当然,redis有时间限制,到了时间用户可以再次请求接口的。

 

 

 

你可能感兴趣的:(java,Redis,java,redis)