springboot 接口防刷

末蓝、 2022-09-08 14:52 275阅读 0赞

springboot 接口防刷

*****************

接口防刷

刷接口造成的影响

  1. 循环访问接口,会使流量暴增,影响网站的正常对外服务
  2. 如果短信验证、支付接口遭到恶意访问,还会造成企业损失

接口防刷措施

  1. 限制ip访问频率与并发数,可用nignx进行限制
  2. limit_req_zone $binary_remote_addr zone=one:10m rate=20r/s; #单个ip每秒请求数
  3. limit_conn_zone $binary_remote_addr zone=addr:10m; #单个ip每秒并发数
  4. 接口添加验证码,如短信接口发送短信前先使用验证码验证
  5. 重要接口在用户认证后才能访问,非认证用户不能访问
  6. 后端对接口限流,限制同一用户访问频率,可使用guavaRedisson提供的限流工具

*****************

示例:**后端接口限流**

***********

配置文件

application.yml

  1. spring:
  2. redis:
  3. host: 192.168.57.120
  4. port: 6379

***********

dto 层

ResponseResult

  1. @Data
  2. public class ResponseResult<T> {
  3. private String code;
  4. private String status;
  5. private String message;
  6. private T data;
  7. }

***********

annotation 层

RateLimiter

  1. @Documented
  2. @Target(ElementType.METHOD)
  3. @Retention(RetentionPolicy.RUNTIME)
  4. public @interface RateLimiter {
  5. int userLimit() default 5;
  6. int ipLimit() default 10;
  7. }

***********

interceptor 层

CustomInterceptor

  1. @Configuration
  2. public class CustomInterceptor implements HandlerInterceptor {
  3. @Resource
  4. private RedissonClient client;
  5. private final ConcurrentHashMap<String,RRateLimiter> map = new ConcurrentHashMap<>();
  6. @Override
  7. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  8. if (handler instanceof HandlerMethod){
  9. Method method = ((HandlerMethod) handler).getMethod();
  10. Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
  11. String userName = authentication.getName();
  12. String path=request.getServletPath();
  13. if (method.isAnnotationPresent(RateLimiter.class)){
  14. RateLimiter rateLimiter = method.getAnnotation(RateLimiter.class);
  15. RRateLimiter limiter;
  16. if (userName!=null && !userName.equals("anonymousUser")){
  17. int userLimit = rateLimiter.userLimit();
  18. String key=userName+path+"user-rate-limiter";
  19. if (!map.containsKey(key)){
  20. limiter = client.getRateLimiter(key);
  21. limiter.setRate(RateType.PER_CLIENT,userLimit,1, RateIntervalUnit.MINUTES);
  22. map.put(key,limiter);
  23. }else {
  24. limiter = map.get(key);
  25. }
  26. }else {
  27. int ipLimiter = rateLimiter.ipLimit();
  28. String remoteAddr = request.getRemoteAddr();
  29. String key=remoteAddr+path+"ip-rate-limiter";
  30. if (!map.containsKey(key)){
  31. limiter = client.getRateLimiter(key);
  32. limiter.setRate(RateType.PER_CLIENT,ipLimiter,1, RateIntervalUnit.MINUTES);
  33. map.put(key,limiter);
  34. }else {
  35. limiter = map.get(key);
  36. }
  37. }
  38. if (limiter.tryAcquire()){
  39. return true;
  40. }else {
  41. throw new RuntimeException("请稍后重试");
  42. }
  43. }
  44. }
  45. return true;
  46. }
  47. }

***********

service 层

CustomUserService

  1. @Service
  2. public class CustomUserService implements UserDetailsService {
  3. @Resource
  4. private PasswordEncoder passwordEncoder;
  5. @Override
  6. public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
  7. return new User("gtlx",passwordEncoder.encode("123456"),
  8. Collections.singletonList(new SimpleGrantedAuthority("user")));
  9. }
  10. }

***********

config 层

DataConfig

  1. @Configuration
  2. public class DataConfig {
  3. @Resource
  4. private RedisProperties redisProperties;
  5. @Bean
  6. public RedissonClient intiRedissonClient(){
  7. Config config=new Config();
  8. config.useSingleServer()
  9. .setAddress("redis://"+redisProperties.getHost()+":"+redisProperties.getPort());
  10. return Redisson.create(config);
  11. }
  12. }

WebConfig

  1. @Configuration
  2. public class WebConfig implements WebMvcConfigurer {
  3. @Resource
  4. private CustomInterceptor customInterceptor;
  5. @Override
  6. public void addInterceptors(InterceptorRegistry registry) {
  7. registry.addInterceptor(customInterceptor);
  8. }
  9. }

WebSecurityConfig

  1. @Configuration
  2. public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  3. @Resource
  4. protected UserDetailsService userDetailsService;
  5. @Override
  6. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  7. auth.userDetailsService(userDetailsService);
  8. }
  9. @Override
  10. protected void configure(HttpSecurity http) throws Exception {
  11. http.formLogin().and().authorizeRequests()
  12. .antMatchers("/hello").authenticated()
  13. .anyRequest().permitAll();
  14. }
  15. @Bean
  16. public PasswordEncoder initPasswordEncoder(){
  17. return new BCryptPasswordEncoder();
  18. }
  19. }

***********

controller 层

HelloController

  1. @RestController
  2. public class HelloController {
  3. @RateLimiter(userLimit = 5)
  4. @RequestMapping("/hello")
  5. public ResponseResult<String> hello(){
  6. ResponseResult<String> result=new ResponseResult<>();
  7. result.setCode("000000");
  8. result.setStatus("success");
  9. result.setData("hello");
  10. return result;
  11. }
  12. @RateLimiter()
  13. @RequestMapping("/hello2")
  14. public ResponseResult<String> hello2(){
  15. ResponseResult<String> result=new ResponseResult<>();
  16. result.setCode("000000");
  17. result.setStatus("success");
  18. result.setData("hello2");
  19. return result;
  20. }
  21. @ExceptionHandler
  22. public ResponseResult<String> handleError(Exception e){
  23. ResponseResult<String> result=new ResponseResult<>();
  24. result.setCode("111111");
  25. result.setStatus("error");
  26. result.setMessage(e.getMessage());
  27. return result;
  28. }
  29. }

*****************

使用测试

localhost:8080/hello

  1. ![watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBAb1_nk5znlLDmnY7kuItfbw_size_9_color_FFFFFF_t_70_g_se_x_16][]

认证通过后,输出

  1. ![watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBAb1_nk5znlLDmnY7kuItfbw_size_9_color_FFFFFF_t_70_g_se_x_16 1][]

连续点击5次,输出

  1. ![watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBAb1_nk5znlLDmnY7kuItfbw_size_9_color_FFFFFF_t_70_g_se_x_16 2][]

localhost:8080/hello2(重启应用)

  1. ![watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBAb1_nk5znlLDmnY7kuItfbw_size_9_color_FFFFFF_t_70_g_se_x_16 3][]

连续点击10次(ip限流),输出

  1. ![watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBAb1_nk5znlLDmnY7kuItfbw_size_9_color_FFFFFF_t_70_g_se_x_16 4][]

发表评论

表情:
评论列表 (有 0 条评论,275人围观)

还没有评论,来说两句吧...

相关阅读

    相关 SpringBoot API 接口

    顾名思义,想让某个接口某个人在某段时间内只能请求N次。 在项目中比较常见的问题也有,那就是连点按钮导致请求多次,以前在web端有表单重复提交,可以通过token 来解决。

    相关 Springboot项目的接口

    说明:使用了注解的方式进行对接口防刷的功能,非常高大上,本文章仅供参考 一,技术要点:springboot的基本知识,redis基本操作,  首先是写一个注解类: