002 springboot redis 防止表单重复提交

server/2024/9/22 18:35:29/

文章目录

    • RedisConfig.java
    • WebConfiguration.java
    • AutoIdempotentTokenController.java
    • MyOrderController.java
    • MyOrder.java
    • AutoIdempotentInterceptor.java
    • AutoIdempotent
    • IdempotentTokenService.java
    • IdempotentTokenServiceImpl.java
    • SpringbootRedisDemoApplication.java
    • application.yaml
    • order_save.jsp

RedisConfig.java

package com.example.config;import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.*;import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;/*** 配置redistemplate序列化*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {//过期时间-1天private Duration timeToLive = Duration.ofDays(-1);/*** RedisTemplate 先关配置** @param factory* @return*/@Bean@SuppressWarnings("all")public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();template.setConnectionFactory(factory);Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);//LocalDatetime序列化JavaTimeModule timeModule = new JavaTimeModule();timeModule.addDeserializer(LocalDate.class,new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));timeModule.addDeserializer(LocalDateTime.class,new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));timeModule.addSerializer(LocalDate.class,new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));timeModule.addSerializer(LocalDateTime.class,new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);om.registerModule(timeModule);jackson2JsonRedisSerializer.setObjectMapper(om);StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();// key采用String的序列化方式template.setKeySerializer(stringRedisSerializer);// hash的key也采用String的序列化方式template.setHashKeySerializer(stringRedisSerializer);// value序列化方式采用jacksontemplate.setValueSerializer(jackson2JsonRedisSerializer);// hash的value序列化方式采用jacksontemplate.setHashValueSerializer(jackson2JsonRedisSerializer);template.afterPropertiesSet();return template;}@Beanpublic RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {//默认1RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(timeToLive).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer())).disableCachingNullValues();RedisCacheManager redisCacheManager = RedisCacheManager.builder(connectionFactory).cacheDefaults(config).transactionAware().build();return redisCacheManager;}@BeanRedisMessageListenerContainer listenerContainer(RedisConnectionFactory connectionFactory) {RedisMessageListenerContainer listenerContainer = new RedisMessageListenerContainer();listenerContainer.setConnectionFactory(connectionFactory);return listenerContainer;}/*** key 类型* @return*/private RedisSerializer<String> keySerializer() {return  new StringRedisSerializer();}/*** 值采用JSON序列化* @return*/private RedisSerializer<Object> valueSerializer() {return new GenericJackson2JsonRedisSerializer();}}

WebConfiguration.java

package com.example.config;import com.example.interceptor.AutoIdempotentInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@EnableWebMvc
@Configuration
public class WebConfiguration implements WebMvcConfigurer {@Autowiredprivate AutoIdempotentInterceptor autoIdempotentInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// "/" 项目根目录(http://localhost:80/app)registry.addInterceptor(autoIdempotentInterceptor).addPathPatterns("/**") //所有资源,包括静态资源.excludePathPatterns("/js/**").excludePathPatterns("/error").excludePathPatterns("/order_save.jsp");}// 静态资源配置@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/**").addResourceLocations("/","classpath:webapp/");WebMvcConfigurer.super.addResourceHandlers(registry);}}

AutoIdempotentTokenController.java


package com.example.controller;import com.example.util.IdempotentTokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;@Controller
public class AutoIdempotentTokenController {@Autowiredprivate IdempotentTokenService tokenService;//获得token@GetMapping("getIdempodentToken")@ResponseBodypublic String getIdempodentToken(){System.out.println("创建token");return tokenService.createToken();}}

MyOrderController.java


package com.example.controller;import com.example.entity.MyOrder;
import com.example.util.AutoIdempotent;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping
public class MyOrderController {@AutoIdempotent  // 检查表单是否重复提交 IdempotentTokenService.checkToken(..) true 放行@PostMapping("myorder")public String save(MyOrder myOrder){System.out.println("接收到了订单信息,正在生成订单service-mapper");return myOrder.toString();}}

MyOrder.java


package com.example.entity;import java.time.LocalDateTime;public class MyOrder {private Integer orderId;private Integer custId;private Integer proId;private String proName;private Float proPrice;private String proColor;private Float proCapacity;private Integer mycouponId;private Float couponPrice;private Float orderPrice;private Integer proCount;private Integer status;private Integer version;private LocalDateTime createTime;private LocalDateTime updateTime;private String other1;private String other2;}

AutoIdempotentInterceptor.java


package com.example.interceptor;import com.example.util.AutoIdempotent;
import com.example.util.IdempotentTokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;@Component
public class AutoIdempotentInterceptor implements HandlerInterceptor {//在这个AutoIdempotentInterceptor拦截器中,preHandle方法用于在请求处理之前执行一些逻辑。////if( !(handler instanceof HandlerMethod)) { return true; } 这一行代码的作用是检查当前的处理器handler是否是一个HandlerMethod的实例。////handler instanceof HandlerMethod: 这是一个Java的类型检查表达式,用于检查handler是否是HandlerMethod类型或其子类型的实例。//!: 这是逻辑非运算符,用于取反。因此,!(handler instanceof HandlerMethod) 表示handler不是HandlerMethod类型或其子类型的实例。//return true;: 如果handler不是HandlerMethod类型或其子类型的实例,则直接返回true,表示放行,不执行后续的拦截逻辑。//简而言之,这行代码的意思是:如果当前的处理器handler不是一个HandlerMethod的实例,那么就不执行后续的拦截逻辑,直接放行。这通常用于排除一些不需要进行幂等性检查的请求处理器。//    @Autowired
//    private IdempotentTokenService idempotentTokenService;
//
//
//
//
//    public boolean preHandle(HttpServletRequest request,Object handler) throws Exception {
//        if(!(handler instanceof HandlerMethod)) {
//            return true;
//        }
//
//
//        Method method = ((HandlerMethod) handler).getMethod();
//        if(method.getAnnotation(AutoIdempotent.class) != null) {
//            boolean result = idempotentTokenService.checkToken(request);
//            if(result){//true:放行,继续执行后面的业务,false:已拦截,不继续执行业务
//                System.out.println("这是第一次提交表单,可以放行");
//                return true;
//            }else {
//                System.out.println("这是重复提交表单,不可以放行");
//                return false;
//            }
//        }
//        return true;//}@Autowiredprivate IdempotentTokenService tokenService;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("======AutoIdempotentInterceptor 拦截器正在拦截=======");if (!(handler instanceof HandlerMethod))return true; // 放行,不拦截HandlerMethod handlerMethod = (HandlerMethod) handler;//        Method method = handlerMethod.getMethod();
//        AutoIdempotent methodAnnotation =  method.getAnnotation(AutoIdempotent.class);
//        if(methodAnnotation !=null){
//            // 验证token 是否有效
//            return tokenService.checkToken(request);
//        }Method method = ((HandlerMethod) handler).getMethod();if (method.getAnnotation(AutoIdempotent.class) != null) {boolean result = tokenService.checkToken(request);if (result) {//true:放行,继续执行后面的业务,false:已拦截,不继续执行业务System.out.println("这是第一次提交表单,可以放行");return true;} else {System.out.println("这是重复提交表单,不可以放行");return false;}}return true;}}

AutoIdempotent


package com.example.util;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoIdempotent {
}

IdempotentTokenService.java


package com.example.util;import javax.servlet.http.HttpServletRequest;/*** 服务器生成幂等token值与验证 幂等token*/
public interface IdempotentTokenService {//1.生成幂等tokenpublic String createToken();//2.验证 幂等token是否有效public boolean checkToken(HttpServletRequest request);
}

IdempotentTokenServiceImpl.java


package com.example.util;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;import javax.servlet.http.HttpServletRequest;
import java.util.UUID;
import java.util.concurrent.TimeUnit;@Service
public class IdempotentTokenServiceImpl implements IdempotentTokenService {@Autowiredprivate RedisTemplate<String,Object> redisTemplate;//1.生成幂等token:(1)生成唯一的token(2)redis setnx token...@Overridepublic String createToken() {String token = UUID.randomUUID().toString().replace("-","");redisTemplate.opsForValue().set(token,token,1, TimeUnit.MINUTES);return token;}//2.验证幂等token是否有效@Overridepublic boolean checkToken(HttpServletRequest request) {System.out.println("=====正在验证token========");//(1)获得tokenString token = request.getParameter("toke");System.out.println("获得到的幂等token的值是:"+token);//(2)Redis 验证token是否存在,若存在,说明有效的请求,并Redis中删除该tokenif(redisTemplate.hasKey(token)){redisTemplate.delete(token);return true;}else {//(3)若token不存在,说明是重复的请求,重定向到指定的页面(或抛异常)System.out.println("该请求是重复的,token不在Redis中");return false;}}
}

SpringbootRedisDemoApplication.java


package com.example;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableScheduling;@SpringBootApplication
@MapperScan("com.example.mapper")
@EnableCaching
@EnableScheduling  // 开启任务调度器
public class SpringbootRedisDemoApplication {public static void main(String[] args) {SpringApplication.run(SpringbootRedisDemoApplication.class, args);}}

application.yaml


server:servlet:context-path: /app  #项目名port: 8080spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/dict?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghaiusername: rootpassword: 123456redis:host: localhostport: 6379database: 0connect-timeout: 1800000jackson:serialization:write-dates-as-timestamps: falselogging:file:path: d://logger #日志记录level:com.example: debug

order_save.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body><script src="js/jquery-3.7.0.min.js"  ></script>
<html>
<head><title>Title</title>
</head>
<body><h2>下订单</h2>
<form action="myorder" method="post">用户id: <input type="text" name="custId">商品id: <input type="text" name="proId">商品名称: <input type="text" name="proName">商品价格: <input type="text" name="proPrice">购买数量: <input type="text" name="proCount"><input type="hidden" name="toke" class="token"><input type="submit" value="去下订单">
</form><script>createIdempodentToken();function createIdempodentToken() {$.ajax({type: "get",url: "getIdempodentToken",success: function (result) {console.log(result)$(".token").val(result)}})}
</script></body>
</html>

http://www.ppmy.cn/server/22579.html

相关文章

【MySQL】1.安装与配置

目录 1.数据库介绍 1.1什么是数据库 1.2数据库分类 2.MySQL服务器安装 2.1Windows绿色安装 2.2Windows中重装MYSQL 3.Mac中常见的安全问题 4.客户端连接MYSQL服务器 5.SQL的分类 1.数据库介绍 1.1什么是数据库 文件保存数据有以下的缺点&#xff1a; 文件的安全性问…

等保测评常见安全风险

在进行等保测评时&#xff0c;需要特别注意的常见安全风险包括但不限于以下几个方面&#xff1a; 1. **信息泄露风险**&#xff1a;评估系统中存储、传输和处理的敏感信息的安全性&#xff0c;防止数据被非法获取&#xff0c;避免个人隐私泄露或公司机密泄露等问题。 2. **拒…

4.Docker本地镜像发布至阿里云仓库、私有仓库、DockerHub

文章目录 0、镜像的生成方法1、本地镜像发布到阿里云仓库2、本地镜像发布到私有仓库3、本地镜像发布到Docker Hub仓库 Docker仓库是集中存放镜像的地方&#xff0c;分为公共仓库和私有仓库。 注册服务器是存放仓库的具体服务器&#xff0c;一个注册服务器上可以有多个仓库&…

uniapp真机调试无法调用之前页面的方法

在uniapp通过getCurrentPages&#xff08;&#xff09;页面栈调用之前页面方法&#xff0c;h5可生效但app真机调试找不到方法 let pages getCurrentPages()let beforePage pages[pages.length - 3]beforePage.refresh() //真机调试refresh为undefined解决&#xff1a; 后面…

【vscode环境配置系列】vscode远程debug配置

VSCODE debug环境配置 插件安装配置文件debug 插件安装 安装C/C, C/C Runner 配置文件 在项目下建立.vscode文件夹&#xff0c;然后分别建立c_cpp_properties.json&#xff0c; launch.json&#xff0c;tasks.json&#xff0c;内容如下&#xff1a; c_cpp_properties.json:…

Rust检查一个Vec<String>是否包含一个特定的子字符串

在Rust中&#xff0c;你可以使用contains方法来检查一个Vec<&str>是否包含特定的字符串。但是&#xff0c;如果你想检查一个Vec是否包含一个特定的子字符串&#xff0c;你需要先将子字符串转换为String。 以下是一个示例代码&#xff0c;展示了如何检查一个Vec是否包…

一文搞懂Python中staticmethod和classmethod的区别

Python 中的类方法是什么&#xff1f; 内置函数修饰符 classmethod 是一个表达式&#xff0c;在你的函数定义之后立即被评估。这种评估结果会掩盖你的函数定义。类似于实例方法接收实例一样&#xff0c;类方法也将类作为隐式的第一个参数。 Python 类方法的语法&#xff1a; …

Ubuntu系统重装

1、删除相关卷&#xff0c;ubuntu引导项&#xff08;select disk、assign letter的方法&#xff09; Ubuntu20.04重装系统过程&#xff08;多图&#xff0c;含保存文件卸载旧系统安装新系统&#xff09;_ubuntu重装系统-CSDN博客 2、分配空间 efi 1024MB 逻辑分区 swap 8*1…