欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 新车 > redis springAop防重点击

redis springAop防重点击

2025/4/18 13:07:38 来源:https://blog.csdn.net/2509_90931430/article/details/147066436  浏览:    关键词:redis springAop防重点击
  • 配置 maven
 <!-- Spring Boot 启动父依赖 --><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.13.RELEASE</version></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><exclusions><exclusion><artifactId>io.lettuce</artifactId><groupId>lettuce-core</groupId></exclusion></exclusions></dependency><dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>2.9.3</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.12.0</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version></dependency></dependencies>
  • application.yml
spring:redis:host: 192.168.56.10port: 6379
  • 注解
    • value 这个值在测试的时候用3秒 方便测试

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 RepeatSubmit {/*** 重复提交间隔时间/毫秒** @return*/int value() default 3000;/*** 最长间隔30s*/int MAX_INTERVAL = 30000;
}
  • 抽象类
    • 获取票据 获得时间戳 这个时间戳是为了和当前时间作比较
    • 如果从redis里拿到key(票据)时间和当前时间比较 看是否点击过了

import java.util.function.Function;/*** 凭证**/
public abstract class AbstractRepeatSubmitTicket {private Function<String, String> ticketFunction;public AbstractRepeatSubmitTicket(Function<String, String> ticketFunction) {this.ticketFunction = ticketFunction;}/*** 获取凭证** @param ticketToken* @return*/public String getTicket(String ticketToken) {return this.ticketFunction.apply(ticketToken);}/*** 获取凭证 时间戳** @param ticket* @return*/public abstract Long getTicketTimestamp(String ticket);/*** 设置本次请求时间** @param ticket*/public abstract void putTicket(String ticket);/*** 移除凭证** @param ticket*/public abstract void removeTicket(String ticket);
}
  • redis实现类

import com.gouying.anno.RepeatSubmit;
import org.springframework.data.redis.core.ValueOperations;import java.util.concurrent.TimeUnit;
import java.util.function.Function;/*** 凭证(redis实现)*/
public class RepeatSubmitRedisTicket extends AbstractRepeatSubmitTicket {private ValueOperations<String, String> redisValueOperations;public RepeatSubmitRedisTicket(ValueOperations<String, String> redisValueOperations, Function<String, String> ticketFunction) {super(ticketFunction);this.redisValueOperations = redisValueOperations;}@Overridepublic Long getTicketTimestamp(String ticket) {Long timeStamp = System.currentTimeMillis();boolean setFlag = redisValueOperations.setIfAbsent(ticket, String.valueOf(timeStamp), RepeatSubmit.MAX_INTERVAL, TimeUnit.MILLISECONDS);if (!setFlag) {timeStamp = Long.valueOf(redisValueOperations.get(ticket));}return timeStamp;}@Overridepublic void putTicket(String ticket) {redisValueOperations.getOperations().delete(ticket);this.getTicketTimestamp(ticket);}@Overridepublic void removeTicket(String ticket) {redisValueOperations.getOperations().delete(ticket);}
}
  • redis配置
    • 这里ticket()就是redis向父抽象提交的function
import com.gouying.abs.RepeatSubmitRedisTicket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;import javax.annotation.Resource;/*** 重复提交配置*/
@Configuration
public class RepeatSubmitConfig {@Resource(name = "redisTemplate")private ValueOperations<String, String> valueOperations;@Beanpublic RepeatSubmitAspect repeatSubmitAspect() {RepeatSubmitRedisTicket redisTicket = new RepeatSubmitRedisTicket(valueOperations,this::ticket);return new RepeatSubmitAspect(redisTicket);}@Beanpublic RedisTemplate<String, String> redisTemplate(RedisConnectionFactory connectionFactory) {RedisTemplate<String, String> template = new RedisTemplate<>();template.setConnectionFactory(connectionFactory);return template;}/*** 获取指明某个用户的凭证* 这个就是redis的function*/private String ticket(String servletPath) {// 获取用户ID:用 sa-token 鉴权的,可自行修改String userId = "zs";if (null == userId) {return "";}return servletPath + "_" + userId;}
}
  • Aspect

import com.gouying.abs.AbstractRepeatSubmitTicket;
import com.gouying.anno.RepeatSubmit;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import java.lang.reflect.Method;/*** 重复提交 aop 切口**/
@Aspect
@Slf4j
public class RepeatSubmitAspect {private AbstractRepeatSubmitTicket repeatSubmitTicket;/*** 获取凭证信息** @param repeatSubmitTicket*/public RepeatSubmitAspect(AbstractRepeatSubmitTicket repeatSubmitTicket) {this.repeatSubmitTicket = repeatSubmitTicket;}/*** 定义切入点**/@Around("@annotation(com.gouying.anno.RepeatSubmit)")public Object around(ProceedingJoinPoint point) throws Throwable {ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();String ticketToken = attributes.getRequest().getServletPath();String ticket = this.repeatSubmitTicket.getTicket(ticketToken);if (StringUtils.isEmpty(ticket)) {return point.proceed();}Long lastRequestTime = this.repeatSubmitTicket.getTicketTimestamp(ticket);if (lastRequestTime != null) {Method method= ((MethodSignature) point.getSignature()).getMethod();RepeatSubmit annotation= method.getAnnotation(RepeatSubmit.class);int interval = Math.min(annotation.value(), RepeatSubmit.MAX_INTERVAL);if (System.currentTimeMillis() < lastRequestTime + interval) {// 提交频繁return "REPEAT_SUBMIT";}}Object obj = null;try {// 先给 ticket 设置在执行中this.repeatSubmitTicket.putTicket(ticket);obj = point.proceed();} catch (Throwable throwable) {log.error("", throwable);throw throwable;}return obj;}}
  • Controller

import com.gouying.anno.RepeatSubmit;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class TestController{@GetMapping("/test1")@RepeatSubmitpublic String test1(String str) {return "防止重复提交:" + str;}@GetMapping("/test3")@RepeatSubmitpublic String test3(String str) {return "防止重复提交3:" + str;}@GetMapping("test2")public String test2(String str) {return "正常提交:" + str;}
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词