当前位置:   article > 正文

【SpringBoot】防止API接口被刷(频繁访问),以及代码执行sql脚本_接口提示访问频繁怎么解决

接口提示访问频繁怎么解决

前言

最近在b站看vue的实战视频,其中需要搭建服务端的api接口,以给vue请求使用。学习资料里面提供了api的项目代码。在本地运行,每次都要启动,有点麻烦。使用老师搭建的api,由于是公用的,数据库数据经常被其他学习的同学删除或修改。

为了一劳永逸,也为了各位小伙伴一起愉快学习。我就用SpringBoot写了一个小程序,放到服务器运行。每天凌晨2点定时执行sql脚本,将数据库数据重新导入。同时为了紧急情况,提供了一个公开访问的api接口,随时可以通过访问该api,将数据库的数据重置。这样,如果一起使用相同的服务端,再也不怕数据的被其他小伙伴改掉了。随时就可以自己重置。

但是重置数据库的操作,是完整地执行整个sql脚本,完整执行需要差不多1分钟的时间,极其占用数据库和服务器资源。如果重置数据库的接口直接暴露,可能会被其他人恶意访问(狂刷)。那么我的服务器会崩的,所以必需给重置数据库的接口限制防刷。其实也比较简单,不多说了,直接看代码。

重置数据库数据(执行sql脚本)

DataController

  1. package net.ysq.vueshopdata.controller;
  2. import net.ysq.vueshopdata.component.Delay;
  3. import net.ysq.vueshopdata.service.DataService;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.scheduling.annotation.EnableScheduling;
  8. import org.springframework.scheduling.annotation.Scheduled;
  9. import org.springframework.stereotype.Controller;
  10. import org.springframework.web.bind.annotation.RequestMapping;
  11. import org.springframework.web.bind.annotation.ResponseBody;
  12. import java.sql.SQLException;
  13. /**
  14. * @author passerbyYSQ
  15. * @create 2020-08-27 23:22
  16. */
  17. @Controller
  18. @ResponseBody
  19. @EnableScheduling // 2.开启定时任务
  20. @RequestMapping("/vueshop")
  21. public class DataController {
  22. private Logger logger = LoggerFactory.getLogger(getClass());
  23. @Autowired
  24. private DataService dataService;
  25. @RequestMapping("log")
  26. public void testLog() {
  27. logger.info("测试log");
  28. }
  29. @Scheduled(cron = "0 0 2 * * ?") // 定时任务,每天凌晨2点,执行该方法
  30. @Delay(time = 1000 * 60) // 使用自定义注解。接口被成功访问后的1分钟之内,其他请求均拦截并驳回
  31. @RequestMapping("/reset")
  32. public String resetDataBase() throws SQLException {
  33. logger.info("【开始】重置数据库vue_shop的数据");
  34. long start = System.currentTimeMillis();
  35. dataService.resetDataBase("mydb.sql");
  36. long end = System.currentTimeMillis();
  37. String cost = "用时:" + (end - start) / 1000.0 + "秒";
  38. logger.info("【结束】重置成功:" + cost);
  39. return cost;
  40. }
  41. }

DataService

  1. package net.ysq.vueshopdata.service;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.core.io.ClassPathResource;
  4. import org.springframework.core.io.support.EncodedResource;
  5. import org.springframework.jdbc.datasource.init.ScriptUtils;
  6. import org.springframework.stereotype.Service;
  7. import javax.sql.DataSource;
  8. import java.nio.charset.StandardCharsets;
  9. import java.sql.Connection;
  10. import java.sql.SQLException;
  11. /**
  12. * @author passerbyYSQ
  13. * @create 2020-08-28 0:16
  14. */
  15. @Service // 不要忘了加入IOC容器,否则无法使用@Autowired注入
  16. public class DataService {
  17. @Autowired
  18. private DataSource dataSource;
  19. /**
  20. * 重置数据库数据
  21. * @param sqlScriptName 需要放在resources文件夹下面
  22. */
  23. public boolean resetDataBase(String sqlScriptName) {
  24. Connection conn = null;
  25. try {
  26. // 从Druid数据源(数据库连接池)中获取一个数据库连接
  27. conn = dataSource.getConnection();
  28. ClassPathResource rc = new ClassPathResource(sqlScriptName);
  29. EncodedResource er = new EncodedResource(rc, StandardCharsets.UTF_8);
  30. // ScriptUtils:是SpringBoot源码中使用的工具类,能够执行Sql脚本
  31. // sql脚本执行中途,遇到错误,默认会抛出异常,停止执行
  32. // 建议传入参数true,忽略中途的错误,但是后面4个参数又是必需的,只需要填入源码中的默认值即可
  33. ScriptUtils.executeSqlScript(conn, er, true, true,
  34. "--", ";", "/*", "*/");
  35. return true;
  36. } catch (Exception e) {
  37. e.printStackTrace();
  38. } finally {
  39. // 不要忘了释放连接
  40. try {
  41. if (conn != null) {
  42. conn.close();
  43. }
  44. } catch (SQLException e) {
  45. e.printStackTrace();
  46. }
  47. }
  48. return false;
  49. }
  50. }

防止API被频繁访问

自定义注解:Delay

  1. package net.ysq.vueshopdata.component;
  2. import org.springframework.stereotype.Component;
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Retention;
  5. import java.lang.annotation.RetentionPolicy;
  6. import java.lang.annotation.Target;
  7. /**
  8. * 该注解放在controller层的方法上(api),
  9. * 用于防止别人恶意访问(刷)一些耗时占资源的接口
  10. * @author passerbyYSQ
  11. * @create 2020-08-28 18:25
  12. */
  13. @Target(ElementType.METHOD)
  14. @Retention(RetentionPolicy.RUNTIME)
  15. @Component
  16. public @interface Delay {
  17. // 默认两秒,表示:当api被成功访问后的2秒内,其他访问请求均拦截并驳回
  18. int time() default 2000;
  19. }

自定义拦截器:RequestFrequencyInterceptor

  1. package net.ysq.vueshopdata.component;
  2. import org.slf4j.Logger;
  3. import org.slf4j.LoggerFactory;
  4. import org.springframework.stereotype.Component;
  5. import org.springframework.web.method.HandlerMethod;
  6. import org.springframework.web.servlet.HandlerInterceptor;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;
  9. import java.util.HashMap;
  10. import java.util.Map;
  11. /**
  12. * @author passerbyYSQ
  13. * @create 2020-08-28 18:26
  14. */
  15. @Component // 加入IOC容器中,以用于在配置类中注册拦截器
  16. public class RequestFrequencyInterceptor implements HandlerInterceptor {
  17. private Logger logger = LoggerFactory.getLogger(getClass());
  18. /**
  19. * 由于该注解可能加到多个方法(接口)上,每个接口上一次的访问时间都不一样。
  20. * key值:必须能够唯一标识方法(接口),由于不同的类中可能会出现同名的方法,所以
  21. * 并不建议直接使用方法名作为key值,
  22. * value值:接口上一次被成功访问(驳回的访问不算)的时间
  23. */
  24. private Map<String, Long> lastTimeMap = new HashMap<>();
  25. @Override
  26. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  27. if(handler instanceof HandlerMethod) {
  28. HandlerMethod handlerMethod = (HandlerMethod) handler;
  29. // 作为标识该接口的key值
  30. String methodKey = handlerMethod.getBean().getClass().getName();
  31. // 如果该方法(接口)上有Delay注解,则获取
  32. Delay delay = handlerMethod.getMethodAnnotation(Delay.class);
  33. if (delay != null) {
  34. Long currentTime = System.currentTimeMillis();
  35. if (!lastTimeMap.containsKey(methodKey)) {
  36. // 接口被第一次访问,没有lastTime
  37. lastTimeMap.put(methodKey, currentTime);
  38. // 放行请求
  39. return true;
  40. } else {
  41. // 方法正在被频繁访问,访问间隔小于delay.time(),请稍后重试
  42. if (currentTime - lastTimeMap.get(methodKey) < delay.time()) {
  43. logger.info("【频繁访问】,已被拦截");
  44. String responseMsg = String.format("接口正在被频繁访问,访问间隔小于%d秒。您已被拦截,请稍后重试", delay.time() / 1000);
  45. response.setContentType("application/json;charset=utf-8"); // 不设置中文会出现乱码
  46. response.getWriter().write(responseMsg);
  47. // 拦截
  48. return false;
  49. } else {
  50. // 大于间隔,更新接口上一次被成功访问的时间,并放行请求
  51. lastTimeMap.put(methodKey, currentTime);
  52. return true;
  53. }
  54. }
  55. }
  56. }
  57. // 该拦截器只处理访问被Delay注解标识的接口的请求,访问其他接口的请求不作拦截,一律放行
  58. return true;
  59. }
  60. }

WebMvc的配置类:WebMvcConfig,注册上面的拦截器

  1. package net.ysq.vueshopdata.config;
  2. import net.ysq.vueshopdata.component.RequestFrequencyInterceptor;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.web.servlet.config.annotation.CorsRegistry;
  6. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  7. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  8. /**
  9. * @author passerbyYSQ
  10. * @create 2020-08-28 18:45
  11. */
  12. @Configuration
  13. public class WebMvcConfig implements WebMvcConfigurer {
  14. @Autowired
  15. private RequestFrequencyInterceptor frequencyInterceptor;
  16. /**
  17. * 注册拦截器RequestFrequencyInterceptor
  18. * @param registry
  19. */
  20. @Override
  21. public void addInterceptors(InterceptorRegistry registry) {
  22. registry.addInterceptor(frequencyInterceptor)
  23. .addPathPatterns( "/**" ).excludePathPatterns("/error");
  24. }
  25. /**
  26. * 支持跨域
  27. * @param registry
  28. */
  29. @Override
  30. public void addCorsMappings(CorsRegistry registry) {
  31. registry.addMapping("/**")
  32. .allowedOrigins("*")
  33. .allowedMethods("GET", "POST", "PUT", "OPTIONS", "DELETE", "PATCH")
  34. .allowCredentials(true).maxAge(3600);
  35. }
  36. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/157251?site
推荐阅读
相关标签
  

闽ICP备14008679号