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

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

项目结构如下:

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

一、编写注解类 AccessLimit

packagecn.mygweb.annotation;

importjava.lang.annotation.ElementType;
importjava.lang.annotation.Retention;
importjava.lang.annotation.RetentionPolicy;
importjava.lang.annotation.Target;

/**
*访问控制注解(实现接口防刷功能)
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public@interfaceAccessLimit{
/**
*限制周期(单位为秒)
*
*@return
*/
intseconds();

/**
*规定周期内限制次数
*
*@return
*/
intmaxCount();

/**
*是否需要登录
*
*@return
*/
booleanneedLogin()defaultfalse;
}

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

packagecn.mygweb.interceptor;

importcn.mygweb.annotation.AccessLimit;
importcn.mygweb.entity.Result;
importcn.mygweb.entity.StatusCode;
importcom.alibaba.fastjson.JSON;
importorg.springframework.stereotype.Component;
importorg.springframework.web.method.HandlerMethod;
importorg.springframework.web.servlet.handler.HandlerInterceptorAdapter;

importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importjava.io.OutputStream;
importjava.util.HashMap;
importjava.util.Map;

/**
*访问控制拦截器
*/
@Component
publicclassAccessLimitInterceptorextendsHandlerInterceptorAdapter{

//模拟数据存储,实际业务中可以自定义实现方式
privatestaticMap<String,AccessInfo>accessInfoMap=newHashMap<>();

@Override
publicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,
Objecthandler)throwsException{
//判断请求是否属于方法的请求
if(handlerinstanceofHandlerMethod){
HandlerMethodhm=(HandlerMethod)handler;

//获取方法中的注解,看是否有该注解
AccessLimitaccessLimit=hm.getMethodAnnotation(AccessLimit.class);
if(accessLimit==null){
returntrue;
}
intseconds=accessLimit.seconds();
intmaxCount=accessLimit.maxCount();
booleanneedLogin=accessLimit.needLogin();
Stringkey=request.getRequestURI();
//如果需要登录
if(needLogin){
//获取登录的session进行判断
//……
key+=""+"userA";//这里假设用户是userA,实际项目中可以改为userId
}

//模拟从redis中获取数据
AccessInfoaccessInfo=accessInfoMap.get(key);
if(accessInfo==null){
//第一次访问
accessInfo=newAccessInfo();
accessInfo.setFirstVisitTimestamp(System.currentTimeMillis());
accessInfo.setAccessCount(1);
accessInfoMap.put(key,accessInfo);
}elseif(accessInfo.getAccessCount()<maxCount){
//访问次数加1
accessInfo.setAccessCount(accessInfo.getAccessCount()+1);
accessInfoMap.put(key,accessInfo);
}else{
//超出访问次数,判断时间是否超出设定时间
if((System.currentTimeMillis()-accessInfo.getFirstVisitTimestamp())<=seconds*1000){
//如果还在设定时间内,则为不合法请求,返回错误信息
render(response,"达到访问限制次数,请稍后重试!");
returnfalse;
}else{
//如果超出设定时间,则为合理的请求,将之前的请求清空,重新计数
accessInfo.setFirstVisitTimestamp(System.currentTimeMillis());
accessInfo.setAccessCount(1);
accessInfoMap.put(key,accessInfo);
}
}
}
returntrue;
}

/**
*向页面发送消息
*
*@paramresponse
*@parammsg
*@throwsException
*/
privatevoidrender(HttpServletResponseresponse,Stringmsg)throwsException{
response.setContentType("application/json;charset=UTF-8");
OutputStreamout=response.getOutputStream();
Stringstr=JSON.toJSONString(newResult(true,StatusCode.ACCESSERROR,msg));
out.write(str.getBytes("UTF-8"));
out.flush();
out.close();
}

/**
*封装的访问信息对象
*/
classAccessInfo{

/**
*一个计数周期内第一次访问的时间戳
*/
privatelongfirstVisitTimestamp;
/**
*访问次数统计
*/
privateintaccessCount;

publiclonggetFirstVisitTimestamp(){
returnfirstVisitTimestamp;
}

publicvoidsetFirstVisitTimestamp(longfirstVisitTimestamp){
this.firstVisitTimestamp=firstVisitTimestamp;
}

publicintgetAccessCount(){
returnaccessCount;
}

publicvoidsetAccessCount(intaccessCount){
this.accessCount=accessCount;
}

@Override
publicStringtoString(){
return"AccessInfo{"+
"firstVisitTimestamp="+firstVisitTimestamp+
",accessCount="+accessCount+
'}';
}
}
}

三、把Interceptor注册到springboot中

packagecn.mygweb.config;

importcn.mygweb.interceptor.AccessLimitInterceptor;
importorg.springframework.context.annotation.Configuration;
importorg.springframework.web.servlet.config.annotation.InterceptorRegistry;
importorg.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
*拦截器注册配置
*/
@Configuration
publicclassWebConfigimplementsWebMvcConfigurer{

@Override
publicvoidaddInterceptors(InterceptorRegistryregistry){
//注册拦截器
registry.addInterceptor(newAccessLimitInterceptor());
}
}

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

packagecn.mygweb.controller;

importcn.mygweb.annotation.AccessLimit;
importcn.mygweb.entity.Result;
importcn.mygweb.entity.StatusCode;
importorg.springframework.web.bind.annotation.GetMapping;
importorg.springframework.web.bind.annotation.RequestMapping;
importorg.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/access")
publicclassAccessController{

@AccessLimit(seconds=5,maxCount=2)//访问控制,5秒内只能访问2次
@GetMapping
publicResultaccess(){
returnnewResult(true,StatusCode.OK,"访问成功!");
}

}

五、测试访问

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

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

StatusCode类

packagecn.mygweb.entity;

/**
*返回状态码
*/
publicclassStatusCode{
publicstaticfinalintOK=20000;//成功
publicstaticfinalintERROR=20001;//失败
publicstaticfinalintLOGINERROR=20002;//用户名或密码错误
publicstaticfinalintACCESSERROR=20003;//权限不足
publicstaticfinalintREMOTEERROR=20004;//远程调用失败
publicstaticfinalintREPERROR=20005;//重复操作
publicstaticfinalintNOTFOUNDERROR=20006;//没有对应的抢购数据
}

Result类:

packagecn.mygweb.entity;

importjava.io.Serializable;

/**
*响应结果
*/
publicclassResult<T>implementsSerializable{
privatebooleanflag;//是否成功
privateIntegercode;//返回码
privateStringmessage;//返回消息
privateTdata;//返回数据

publicResult(booleanflag,Integercode,Stringmessage,Objectdata){
this.flag=flag;
this.code=code;
this.message=message;
this.data=(T)data;
}

publicResult(booleanflag,Integercode,Stringmessage){
this.flag=flag;
this.code=code;
this.message=message;
}

publicResult(){
this.flag=true;
this.code=StatusCode.OK;
this.message="操作成功!";
}

publicbooleanisFlag(){
returnflag;
}

publicvoidsetFlag(booleanflag){
this.flag=flag;
}

publicIntegergetCode(){
returncode;
}

publicvoidsetCode(Integercode){
this.code=code;
}

publicStringgetMessage(){
returnmessage;
}

publicvoidsetMessage(Stringmessage){
this.message=message;
}

publicTgetData(){
returndata;
}

publicvoidsetData(Tdata){
this.data=data;
}
}

applications.yml:

server:
port:8080

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

发布于 2021-03-02 23:49:28
收藏
分享
海报
0 条评论
176
上一篇:springboot中autowire注入为null如何解决 下一篇:使用Navicat连接MySql数据库时速度慢如何解决
目录

    0 条评论

    本站已关闭游客评论,请登录或者注册后再评论吧~

    忘记密码?

    图形验证码