使用Redis和使用Spring AOP

发布时间:2025-06-24 18:46:27  作者:北方职教升学中心  阅读量:863



传递Token:将Token嵌入到表单中,随表单一起提交。使用Redis和使用Spring AOP。
验证Token:服务器接收到请求后,首先验证Token是否有效,如果有效则继续处理请求,并从Session中移除该Token;如果无效,则返回错误信息。
在这里插入图片描述

使用Token机制

Token机制是一种常见的防重复提交方法。误操作等原因多次点击提交按钮,导致后台接收到多个相同的请求。每种方法都有其适用场景和优缺点,可以根据实际需求选择合适的方法。具体步骤如下:
生成Token:用户每次请求表单页面时,服务器生成一个唯一的Token,并将其存储在Session中。

实现步骤

1.引入Redis依赖

在 pom.xml 中添加Redis依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
2.生成Token

在Controller中生成Token并存储在Redis中:

@Autowiredprivate StringRedisTemplate redisTemplate;/** * formByRedis * @author senfel * @date 2024/11/12 11:50 * @return org.springframework.web.servlet.ModelAndView */@GetMapping("/formByRedis")public ModelAndView showFormByRedis(){ModelAndView form =new ModelAndView("form");String token =UUID.randomUUID().toString();// 设置过期时间    redisTemplate.opsForValue().set(token, token, 5, TimeUnit.MINUTES);form.addObject("token", token);returnform;}
3.传递Token

在表单中添加隐藏字段来传递Token:

<form action="/base/submitByRedis"method="post"><input type="hidden"name="token"th:value="${token}"><!-- 其他表单字段 --><button type="submit">Submit</button></form>
4.验证Token

在Controller中验证Token:

/** * submitByRedis * @param token * @author senfel * @date 2024/11/12 11:50 * @return java.lang.String */@PostMapping("/submitByRedis")public String handleFormByRedis(@RequestParam String token){String redisToken =redisTemplate.opsForValue().get(token);if(redisToken ==null){throw new RuntimeException("Duplicate submit detected");}// 删除Token    redisTemplate.delete(token);// 处理表单数据    return"success";}

使用Spring AOP

Spring AOP(Aspect-Oriented Programming)可以用来实现切面编程,从而在多个方法中复用防重复提交的逻辑。
传递Token:将Token嵌入到表单中,随表单一起提交。

实现步骤

1.定义注解

创建一个自定义注解 @PreventDuplicateSubmit:

importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;/** * PreventDuplicateSubmit * @author senfel * @date 2024/11/12 11:56 */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface PreventDuplicateSubmit {/**重复请求时间*/    int expireSeconds()default 10;}
2.创建切面

创建一个切面类 DuplicateSubmitAspect:

importlombok.extern.slf4j.Slf4j;importorg.aspectj.lang.ProceedingJoinPoint;importorg.aspectj.lang.annotation.Around;importorg.aspectj.lang.annotation.Aspect;importorg.aspectj.lang.reflect.MethodSignature;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.data.redis.core.StringRedisTemplate;importorg.springframework.stereotype.Component;importjava.lang.reflect.Method;importjava.util.concurrent.TimeUnit;/** * DuplicateSubmitAspect * @author senfel * @version 1.0* @date 2024/11/12 11:57 */@Slf4j@Aspect@Componentpublic class DuplicateSubmitAspect {protected static final Logger logger =LoggerFactory.getLogger(DuplicateSubmitAspect.class);@Autowired    private StringRedisTemplate redisTemplate;/**     * around     * @param joinPoint     * @author senfel     * @date 2024/11/12 15:45     * @return java.lang.Object     */    @Around("@annotation(com.example.ccedemo.aop.PreventDuplicateSubmit)")public Object around(ProceedingJoinPoint joinPoint)throws Throwable {StringBuilder key =new StringBuilder();//获取class        String simpleName =joinPoint.getTarget().getClass().getSimpleName();key.append(simpleName);// 获取请求方法        MethodSignature signature=(MethodSignature)joinPoint.getSignature();Method method =signature.getMethod();String methodName =method.getName();key.append(":").append(methodName);//获取请求参数        Object[]args=joinPoint.getArgs();for(Object arg :args){key.append(":").append(arg.toString());}//TODO 获取客户端IP        // 获取注解信息        PreventDuplicateSubmit annotation =method.getAnnotation(PreventDuplicateSubmit.class);// 判断是否已经请求过        if(redisTemplate.hasKey(key.toString())){throw new RuntimeException("请勿重复提交");}//标记请求已经处理过        redisTemplate.opsForValue().set(key.toString(),"1",annotation.expireSeconds(), TimeUnit.SECONDS);returnjoinPoint.proceed();}}
3.使用注解

在Controller方法上使用 @PreventDuplicateSubmit 注解:

/** * handleFormByAnnotation * @param param * @author senfel * @date 2024/11/12 11:59 * @return java.lang.String */@PostMapping("/submitByAnnotation")@PreventDuplicateSubmitpublic String handleFormByAnnotation(@RequestParam String param){// 处理表单数据    return"success";}

总结

本文介绍了三种在Spring Boot中实现接口防重复提交的方法:使用Token机制、这不仅会浪费服务器资源,还可能导致数据不一致等问题。

用户可能会因为网络延迟、

实现步骤

1.生成Token

在Controller中生成Token并存储在Session中:

/** * form * @param session * @author senfel * @date 2024/11/12 11:29 * @return org.springframework.web.servlet.ModelAndView */@GetMapping("/form")public ModelAndView showForm(HttpSession session){ModelAndView form =new ModelAndView("form");String token =UUID.randomUUID().toString();session.setAttribute("token", token);form.addObject("token", token);returnform;}
2.传递Token

在表单中添加隐藏字段来传递Token:

<form action="/base/submit"method="post"><input type="hidden"name="token"th:value="${token}"><!-- 其他表单字段 --><button type="submit">Submit</button></form>
3.验证Token

在Controller中验证Token:

/** * handleForm * @param token * @param session * @author senfel * @date 2024/11/12 11:34 * @return java.lang.String */@PostMapping("/submit")public String handleForm(@RequestParam String token, HttpSession session){String sessionToken =(String)session.getAttribute("token");if(sessionToken ==null ||!sessionToken.equals(token)){throw new RuntimeException("Duplicate submit detected");}// 移除Token    session.removeAttribute("token");// 处理表单数据    return"success";}

使用Redis

Redis是一个高性能的键值存储系统,可以用来存储和验证Token。本文将介绍几种在Spring Boot中实现接口防重复提交的方法。具体步骤如下:
生成Token:用户每次请求表单页面时,服务器生成一个唯一的Token,并将其存储在Redis中。通过这些方法,可以有效防止用户重复提交表单,提高系统的稳定性和用户体验。
验证Token:服务器接收到请求后,首先验证Token是否存在于Redis中,如果存在则继续处理请求,并从Redis中删除该Token;如果不存在,则返回错误信息。

文章目录

    • 前言
    • 使用Token机制
      • 实现步骤
        • 1.生成Token
        • 2.传递Token
        • 3.验证Token
    • 使用Redis
      • 实现步骤
        • 1.引入Redis依赖
        • 2.生成Token
        • 3.传递Token
        • 4.验证Token
    • 使用Spring AOP
      • 实现步骤
        • 1.定义注解
        • 2.创建切面
        • 3.使用注解
    • 总结

前言

在Web开发中,防止用户重复提交表单是一个常见的需求。