服务器端实现方案:同一客户端在2秒内对同一URL的提交视为重复提交

上代码吧

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>springboot-repeat-submit</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>24.0-jre</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

Application.java

package com;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; /**
* @author www.gaozz.club
* @功能描述 防重复提交
* @date 2018-08-26
*/
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

自定义注解NoRepeatSubmit.java

package com.common;

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) // 运行时有效
/**
* @功能描述 防止重复提交标记注解
* @author www.gaozz.club
* @date 2018-08-26
*/
public @interface NoRepeatSubmit {
}

aop解析注解NoRepeatSubmitAop.java

package com.common;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; import com.google.common.cache.Cache; @Aspect
@Component
/**
* @功能描述 aop解析注解
* @author www.gaozz.club
* @date 2018-08-26
*/
public class NoRepeatSubmitAop { private Log logger = LogFactory.getLog(getClass()); @Autowired
private Cache<String, Integer> cache; @Around("execution(* com.example..*Controller.*(..)) && @annotation(nrs)")
public Object arround(ProceedingJoinPoint pjp, NoRepeatSubmit nrs) {
try {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
String sessionId = RequestContextHolder.getRequestAttributes().getSessionId();
HttpServletRequest request = attributes.getRequest();
String key = sessionId + "-" + request.getServletPath();
if (cache.getIfPresent(key) == null) {// 如果缓存中有这个url视为重复提交
Object o = pjp.proceed();
cache.put(key, 0);
return o;
} else {
logger.error("重复提交");
return null;
}
} catch (Throwable e) {
e.printStackTrace();
logger.error("验证重复提交时出现未知异常!");
return "{\"code\":-889,\"message\":\"验证重复提交时出现未知异常!\"}";
} } }

缓存类

package com.common;

import java.util.concurrent.TimeUnit;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder; @Configuration
/**
* @功能描述 内存缓存
* @author www.gaozz.club
* @date 2018-08-26
*/
public class UrlCache {
@Bean
public Cache<String, Integer> getCache() {
return CacheBuilder.newBuilder().expireAfterWrite(2L, TimeUnit.SECONDS).build();// 缓存有效期为2秒
}
}

测试Controller

package com.example;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import com.common.NoRepeatSubmit; /**
* @功能描述 测试Controller
* @author www.gaozz.club
* @date 2018-08-26
*/
@RestController
public class TestController {
@RequestMapping("/test")
@NoRepeatSubmit
public String test() {
return ("程序逻辑返回");
} }

浏览器输入http://localhost:8080/test

然后F5刷新查看效果

 
QQ图片20180914165017.png

以下为新版内容:解决了程序集群部署时请求可能会落到多台机器上的问题,把内存缓存换成了redis


application.yml

spring:
redis:
host: 192.168.1.92
port:
password:

RedisConfig.java

package com.common;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate; @Configuration public class RedisConfig {
@Bean
@ConfigurationProperties(prefix = "spring.redis")
public JedisConnectionFactory getConnectionFactory() {
return new JedisConnectionFactory(new RedisStandaloneConfiguration(), JedisClientConfiguration.builder().build());
} @Bean
<K, V> RedisTemplate<K, V> getRedisTemplate() {
RedisTemplate<K, V> redisTemplate = new RedisTemplate<K, V>();
redisTemplate.setConnectionFactory(getConnectionFactory());
return redisTemplate;
} }

调整切面类NoRepeatSubmitAop.java

package com.common;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; @Aspect
@Component
/**
* @功能描述 aop解析注解
* @author www.gaozz.club
* @date 2018-11-02
*/
public class NoRepeatSubmitAop { private Log logger = LogFactory.getLog(getClass()); @Autowired
private RedisTemplate<String, Integer> template; @Around("execution(* com.example..*Controller.*(..)) && @annotation(nrs)")
public Object arround(ProceedingJoinPoint pjp, NoRepeatSubmit nrs) {
ValueOperations<String, Integer> opsForValue = template.opsForValue();
try {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
String sessionId = RequestContextHolder.getRequestAttributes().getSessionId();
HttpServletRequest request = attributes.getRequest();
String key = sessionId + "-" + request.getServletPath();
if (opsForValue.get(key) == null) {// 如果缓存中有这个url视为重复提交
Object o = pjp.proceed();
opsForValue.set(key, , , TimeUnit.SECONDS);
return o;
} else {
logger.error("重复提交");
return null;
}
} catch (Throwable e) {
e.printStackTrace();
logger.error("验证重复提交时出现未知异常!");
return "{\"code\":-889,\"message\":\"验证重复提交时出现未知异常!\"}";
} } }

转自https://www.jianshu.com/p/09c6b05b670a

spring boot 防止重复提交的更多相关文章

  1. spring mvc 防止重复提交表单的两种方法,推荐第二种

    第一种方法:判断session中保存的token 比较麻烦,每次在提交表单时都必须传入上次的token.而且当一个页面使用ajax时,多个表单提交就会有问题. 注解Token代码: package c ...

  2. spring boot 学习(七)小工具篇:表单重复提交

    注解 + 拦截器:解决表单重复提交 前言 学习 Spring Boot 中,我想将我在项目中添加几个我在 SpringMVC 框架中常用的工具类(主要都是涉及到 Spring AOP 部分知识).比如 ...

  3. Spring Boot (一) 校验表单重复提交

    一.前言 在某些情况下,由于网速慢,用户操作有误(连续点击两下提交按钮),页面卡顿等原因,可能会出现表单数据重复提交造成数据库保存多条重复数据. 存在如上问题可以交给前端解决,判断多长时间内不能再次点 ...

  4. Spring Boot 如何防止重复提交?

    Java技术栈 www.javastack.cn 优秀的Java技术公众号 在传统的web项目中,防止重复提交,通常做法是:后端生成一个唯一的提交令牌(uuid),并存储在服务端.页面提交请求携带这个 ...

  5. spring boot 重复提交

    package com.future.interceptor; import javax.servlet.http.HttpServletRequest; import org.aspectj.lan ...

  6. Spring MVC拦截器+注解方式实现防止表单重复提交

    原理:在新建页面中Session保存token随机码,当保存时验证,通过后删除,当再次点击保存时由于服务器端的Session中已经不存在了,所有无法验证通过. 注,如果是集群的方式,则需要将token ...

  7. Spring MVC实现防止表单重复提交(转)

    Spring MVC拦截器+注解方式实现防止表单重复提交  

  8. Spring MVC防止数据重复提交

    现实开发中表单重复提交的例子很多,就包括手上这个门户的项目也有这种应用场景,用的次数多,但是总结,这还是第一次. 一.基本原理 使用token,给所有的url加一个拦截器,在拦截器里面用java的UU ...

  9. Spring MVC表单防重复提交

    利用Spring MVC的过滤器及token传递验证来实现表单防重复提交. 创建注解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RU ...

随机推荐

  1. Python进阶----网络通信基础 ,OSI七层协议() ,UDP和TCP的区别 , TCP/IP协议(三次握手,四次挥手)

    Python进阶----网络通信基础 ,OSI七层协议() ,UDP和TCP的区别 , TCP/IP协议(三次握手,四次挥手) 一丶CS/BS 架构 C/S: 客户端/服务器    定义:       ...

  2. (转) Python3—UnicodeEncodeError 'ascii' codec can't encode characters in position 0-1

    (转)python(三):Python3-UnicodeEncodeError 'ascii' codec can't encode characters in position 0-1 python ...

  3. react native 集成react navigation报错

    集成后出现:“Invalid escape sequence at line 1 column 29 path $[0].name”的错误. 解决办法:

  4. 使用IDEA运行项目时提示:Warning:java: 源值1.5已过时, 将在未来所有发行版中删除

    如图 在使用IDEA运行项目时,在下方提示:Warning:java: 源值1.5已过时, 将在未来所有发行版中删除 这是因为JDK版本问题 解决方法如下:左上角 file ——> Projec ...

  5. Cypress自动化环境搭建

    1.Cypress 下载: 官网下载,下载后直接解压即可,解压后便可单机exe文件打开 Ps:直接打开exe是会报错找不到json文件的,所以还要安装依赖环境 运行cypress项目前,必须vue-c ...

  6. ubuntu16.04重置root密码

    问题描述: 一个用户不能G附加多个用户组,在centos没有问题的.具体的还不清楚 问题解决:   重启进入启动项修改启动参数进入root中设置密码 参考: https://www.cnblogs.c ...

  7. admin端的教师管理功能测试

    1  概述 1.1   测试范围 本次所测试的内容是admin端的教师管理功能. 1.2   测试方法 采用黑盒子方法进行集成测试. 1.3   测试环境 (1)   服务器l  操作系统:Windo ...

  8. 洛谷 P2384 最短路题解

    题目背景 狗哥做烂了最短路,突然机智的考了Bosh一道,没想到把Bosh考住了...你能帮Bosh解决吗? 他会给你100000000000000000000000000000000000%10金币w ...

  9. Django设置应用名与模型名为中文

    修改polls包里面的apps.py: from django.apps import AppConfig class PollsConfig(AppConfig): name = 'polls' v ...

  10. phantomJS+Python 隐形浏览器

    phantomjs解压后,把文件夹bin中的phantomjs.exe移到python文件夹中的Scripts中 实例: from selenium import webdriver driver = ...