温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

使用注解怎么实现一个SpringBoot 接口防刷功能

发布时间:2021-03-02 16:34:19 来源:亿速云 阅读:197 作者:Leah 栏目:开发技术

这篇文章将为大家详细讲解有关使用注解怎么实现一个SpringBoot 接口防刷功能,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。

项目结构如下:

使用注解怎么实现一个SpringBoot 接口防刷功能

一、编写注解类 AccessLimit

package cn.mygweb.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /**  * 访问控制注解(实现接口防刷功能)  */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface AccessLimit {   /**    * 限制周期(单位为秒)    *    * @return    */   int seconds();   /**    * 规定周期内限制次数    *    * @return    */   int maxCount();   /**    * 是否需要登录    *    * @return    */   boolean needLogin() default false; }

二、在Interceptor拦截器中实现拦截逻辑

package cn.mygweb.interceptor; import cn.mygweb.annotation.AccessLimit; import cn.mygweb.entity.Result; import cn.mygweb.entity.StatusCode; import com.alibaba.fastjson.JSON; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.OutputStream; import java.util.HashMap; import java.util.Map; /**  * 访问控制拦截器  */ @Component public class AccessLimitInterceptor extends HandlerInterceptorAdapter {   //模拟数据存储,实际业务中可以自定义实现方式   private static Map<String, AccessInfo> accessInfoMap = new HashMap<>();   @Override   public boolean preHandle(HttpServletRequest request, HttpServletResponse response,                Object handler) throws Exception {     //判断请求是否属于方法的请求     if (handler instanceof HandlerMethod) {       HandlerMethod hm = (HandlerMethod) handler;       //获取方法中的注解,看是否有该注解       AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);       if (accessLimit == null) {         return true;       }       int seconds = accessLimit.seconds();       int maxCount = accessLimit.maxCount();       boolean needLogin = accessLimit.needLogin();       String key = request.getRequestURI();       //如果需要登录       if (needLogin) {         //获取登录的session进行判断         //……         key += " " + "userA";//这里假设用户是userA,实际项目中可以改为userId       }       //模拟从redis中获取数据       AccessInfo accessInfo = accessInfoMap.get(key);       if (accessInfo == null) {         //第一次访问         accessInfo = new AccessInfo();         accessInfo.setFirstVisitTimestamp(System.currentTimeMillis());         accessInfo.setAccessCount(1);         accessInfoMap.put(key, accessInfo);       } else if (accessInfo.getAccessCount() < maxCount) {         //访问次数加1         accessInfo.setAccessCount(accessInfo.getAccessCount() + 1);         accessInfoMap.put(key, accessInfo);       } else {         //超出访问次数,判断时间是否超出设定时间         if ((System.currentTimeMillis() - accessInfo.getFirstVisitTimestamp()) <= seconds * 1000) {           //如果还在设定时间内,则为不合法请求,返回错误信息           render(response, "达到访问限制次数,请稍后重试!");           return false;         } else {           //如果超出设定时间,则为合理的请求,将之前的请求清空,重新计数           accessInfo.setFirstVisitTimestamp(System.currentTimeMillis());           accessInfo.setAccessCount(1);           accessInfoMap.put(key, accessInfo);         }       }     }     return true;   }   /**    * 向页面发送消息    *    * @param response    * @param msg    * @throws Exception    */   private void render(HttpServletResponse response, String msg) throws Exception {     response.setContentType("application/json;charset=UTF-8");     OutputStream out = response.getOutputStream();     String str = JSON.toJSONString(new Result(true, StatusCode.ACCESSERROR, msg));     out.write(str.getBytes("UTF-8"));     out.flush();     out.close();   }   /**    * 封装的访问信息对象    */   class AccessInfo {     /**      * 一个计数周期内第一次访问的时间戳      */     private long firstVisitTimestamp;     /**      * 访问次数统计      */     private int accessCount;     public long getFirstVisitTimestamp() {       return firstVisitTimestamp;     }     public void setFirstVisitTimestamp(long firstVisitTimestamp) {       this.firstVisitTimestamp = firstVisitTimestamp;     }     public int getAccessCount() {       return accessCount;     }     public void setAccessCount(int accessCount) {       this.accessCount = accessCount;     }     @Override     public String toString() {       return "AccessInfo{" +           "firstVisitTimestamp=" + firstVisitTimestamp +           ", accessCount=" + accessCount +           '}';     }   } }

三、把Interceptor注册到springboot中

package cn.mygweb.config; import cn.mygweb.interceptor.AccessLimitInterceptor; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /**  * 拦截器注册配置  */ @Configuration public class WebConfig implements WebMvcConfigurer {   @Override   public void addInterceptors(InterceptorRegistry registry) {     //注册拦截器     registry.addInterceptor(new AccessLimitInterceptor());   } }

四、在Controller中加入注解实现接口防刷

package cn.mygweb.controller; import cn.mygweb.annotation.AccessLimit; import cn.mygweb.entity.Result; import cn.mygweb.entity.StatusCode; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/access") public class AccessController {   @AccessLimit(seconds = 5, maxCount = 2)//访问控制,5秒内只能访问2次   @GetMapping   public Result access() {     return new Result(true, StatusCode.OK, "访问成功!");   } }

五、测试访问

使用注解怎么实现一个SpringBoot 接口防刷功能

附:StatusCode.java、Result.java、application.yml

StatusCode类

package cn.mygweb.entity; /**  * 返回状态码  */ public class StatusCode {   public static final int OK = 20000;//成功   public static final int ERROR = 20001;//失败   public static final int LOGINERROR = 20002;//用户名或密码错误   public static final int ACCESSERROR = 20003;//权限不足   public static final int REMOTEERROR = 20004;//远程调用失败   public static final int REPERROR = 20005;//重复操作   public static final int NOTFOUNDERROR = 20006;//没有对应的抢购数据 }

Result类:

package cn.mygweb.entity; import java.io.Serializable; /**  * 响应结果  */ public class Result<T> implements Serializable {   private boolean flag;//是否成功   private Integer code;//返回码   private String message;//返回消息   private T data;//返回数据   public Result(boolean flag, Integer code, String message, Object data) {     this.flag = flag;     this.code = code;     this.message = message;     this.data = (T) data;   }   public Result(boolean flag, Integer code, String message) {     this.flag = flag;     this.code = code;     this.message = message;   }   public Result() {     this.flag = true;     this.code = StatusCode.OK;     this.message = "操作成功!";   }   public boolean isFlag() {     return flag;   }   public void setFlag(boolean flag) {     this.flag = flag;   }   public Integer getCode() {     return code;   }   public void setCode(Integer code) {     this.code = code;   }   public String getMessage() {     return message;   }   public void setMessage(String message) {     this.message = message;   }   public T getData() {     return data;   }   public void setData(T data) {     this.data = data;   } }

applications.yml:

server:  port: 8080

关于使用注解怎么实现一个SpringBoot 接口防刷功能就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI