您当前的位置:首页 > IT编程 > python
| C语言 | Java | VB | VC | python | Android | TensorFlow | C++ | oracle | 学术与代码 | cnn卷积神经网络 | gnn | 图像修复 | Keras | 数据集 | Neo4j | 自然语言处理 | 深度学习 | 医学CAD | 医学影像 | 超参数 | pointnet | pytorch | 异常检测 | Transformers | 情感分类 | 知识图谱 |

自学教程:基于注解实现 SpringBoot 接口防刷的方法

51自学网 2021-10-30 22:53:45
  python
这篇教程基于注解实现 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;/** * 访问控制拦截器 */@Componentpublic 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;/** * 拦截器注册配置 */@Configurationpublic 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, "访问成功!");  }}

五、测试访问

在这里插入图片描述

附: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 接口防刷的方法的文章就介绍到这了,更多相关SpringBoot 接口防刷内容请搜索51zixue.net以前的文章或继续浏览下面的相关文章希望大家以后多多支持51zixue.net!


python Autopep8实现按PEP8风格自动排版Python代码
Django项目在pycharm新建的步骤方法
万事OK自学网:51自学网_软件自学网_CAD自学网自学excel、自学PS、自学CAD、自学C语言、自学css3实例,是一个通过网络自主学习工作技能的自学平台,网友喜欢的软件自学网站。