java后端使用token处理表单重复提交
- 保证接口幂等性,表单重复提交
前台解决方案:
提交后按钮禁用、置灰、页面出现遮罩
后台解决方案: 使用token,每个token只能使用一次
1.在调用接口之前生成对应的Token,存放至redis2.在调用接口时,将生成的令牌放入请求request中
3.接口提交的时候获取对应的令牌,如果能够从redis中获得该令牌(获取后将当前令牌删除),
则继续执行访问的业务逻辑4.接口提交的时候获取对应的令牌,如果获取不到改令牌,则直接返回请勿提交
工程源码:https://github.com/youxiu326/sb_more_submit
自定义注解
ApiToken注解用于将token保存至request,用于页面取token
ApiRepeatSubmit注解用于标明改方法需要验证token才能提交
package com.huarui.util; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* 生成token注解
*/
@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiToken {
}
ApiToken.java
package com.huarui.util; import com.huarui.common.ConstantUtils;
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 ApiRepeatSubmit {
ConstantUtils value();
}
ApiRepeatSubmit.java
package com.huarui.common; /**
* 【定义从哪里取Token的枚举类】
* head 即从请求头中取token,即客户端将token放入请求头来请求后端数据
* body 即直接从请求体中取token
*/
public enum ConstantUtils {
BOOD,HEAD
}
ConstantUtils.java
spring.thymeleaf.cache=false spring.redis.host=youxiu326.xin
spring.redis.port=6379
application.properties
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.19.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.huarui</groupId>
<artifactId>sb_more_submit</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sb_more_submit</name>
<description>Demo project for Spring Boot</description> <properties>
<java.version>1.8</java.version>
<thymeleaf.version>3.0.9.RELEASE</thymeleaf.version>
<thymeleaf-layout-dialect.version>2.2.2</thymeleaf-layout-dialect.version>
</properties> <dependencies> <dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>
pom.xml
切面拦截,
package com.huarui.util; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.UUID;
import java.util.concurrent.TimeUnit; /**
* redis工具类
*/
@Component
public class RedisTokenUtils { private long timeout = 2;//过期时间 @Autowired
private RedisTemplate redisTemplate; /**
* 获取Token 并将Token保存至redis
* @return
*/
public String getToken() {
String token = "token_"+ UUID.randomUUID();
redisTemplate.opsForValue().set(token,token,timeout, TimeUnit.MINUTES);
return token;
} /**
* 判断Token是否存在 并且删除Token
* @param tokenKey
* @return
*/
public boolean findToken(String tokenKey){
String token = (String) redisTemplate.opsForValue().get(tokenKey);
if (StringUtils.isEmpty(token)) {
return false;
}
// token 获取成功后 删除对应tokenMapstoken
redisTemplate.delete(tokenKey);
return true;
} }
RedisTokenUtils.java
package com.huarui.aop; import javax.servlet.http.HttpServletRequest;
import com.huarui.common.ConstantUtils;
import com.huarui.util.ApiToken;
import com.huarui.util.ApiRepeatSubmit;
import com.huarui.util.RedisTokenUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
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;
import java.util.concurrent.TimeUnit; /**
* @功能描述 aop解析注解
*/
@Aspect
@Component
public class NoRepeatSubmitAop { private Log logger = LogFactory.getLog(getClass()); @Autowired
private RedisTokenUtils redisTokenUtils; /**
* 将token放入请求
* @param pjp
* @param nrs
*/
@Before("execution(* com.huarui.controller.*Controller.*(..)) && @annotation(nrs)")
public void before(JoinPoint pjp, ApiToken nrs){
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
request.setAttribute("token", redisTokenUtils.getToken());
} /**
* 拦截带有重复请求的注解的方法
* @param pjp
* @param nrs
* @return
*/
@Around("execution(* com.huarui.controller.*Controller.*(..)) && @annotation(nrs)")
public Object arround(ProceedingJoinPoint pjp, ApiRepeatSubmit nrs) { try {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest(); String token = null;
if (nrs.value() == ConstantUtils.BOOD){
//从请求体中取Token
token = (String) request.getAttribute("token");
}else if (nrs.value() == ConstantUtils.HEAD){
//从请求头中取Token
token = request.getHeader("token");
}
if (StringUtils.isEmpty(token)){
return "token 不存在";
}
if (!redisTokenUtils.findToken(token)){
return "请勿重复提交";
}
Object o = pjp.proceed();
return o;
} catch (Throwable e) {
e.printStackTrace();
logger.error("验证重复提交时出现未知异常!");
return "{\"code\":-889,\"message\":\"验证重复提交时出现未知异常!\"}";
} } }
package com.huarui.controller; import com.huarui.common.ConstantUtils;
import com.huarui.util.ApiRepeatSubmit;
import com.huarui.util.ApiToken;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController; @Controller
public class TestController { /**
* 进入页面
* @return
*/
@GetMapping("/")
@ApiToken
public String index(){
return "index";
} /**
* 测试重复提交接口
* 将Token放入请求头中
* @return
*/
@RequestMapping("/test")
@ApiRepeatSubmit(ConstantUtils.HEAD)
public @ResponseBody String test() {
return ("程序逻辑返回");
} }
前端页面:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<base th:href="${#httpServletRequest.getContextPath()+'/'}">
<meta charset="UTF-8">
<title>测试表单重复功能</title>
</head>
<body> <td colspan="1"><button type="button" onclick="add()">加购</button></td> </body> <script src="/jquery-1.11.3.min.js"></script>
<script th:inline="javascript"> function add(){
//取得token参数
var token = [[${token}]];
console.log("获取到的token:" + token);
$.ajax({
type: 'POST',
url: "/test",
data: {},
headers: {
"token":token,
},
// dataType: "json",
success: function(response){
alert(response);
},
error:function(response){
alert(response);
console.log(response);
}
});
} </script> </html>
工程源码:https://github.com/youxiu326/sb_more_submit
java后端使用token处理表单重复提交的更多相关文章
- PHP简单利用token防止表单重复提交
<?php /* * PHP简单利用token防止表单重复提交 * 此处理方法纯粹是为了给初学者参考 */ session_start(); function set_token() { $_S ...
- PHP生成token防止表单重复提交
.提交按钮置disabled 当用户提交后,立即把按钮置为不可用状态.这种用js来实现. 提交前代码如下: $() { $exec="insert into student (user_ ...
- PHP简单利用token防止表单重复提交(转)
<?php/* * PHP简单利用token防止表单重复提交 */function set_token() { $_SESSION['token'] = md5(microtime(true)) ...
- php通过token验证表单重复提交
PHP防止重复提交表单 2016-11-08 轻松学PHP 我们提交表单的时候,不能忽视的一个限制是防止用户重复提交表单,因为有可能用户连续点击了提交按钮或者是攻击者恶意提交数据,那么我们在提交数据后 ...
- AOP+Token防止表单重复提交
表单重复提交: 由于用户误操作,多次点击表单提交按钮 由于网速等原因造成页面卡顿,用户重复刷新提交页面 避免表单重复提交的方式: 1.页面上的按钮做防重复点击操作 2.在数据库中可以做唯一约束 3.利 ...
- PHP使用token防止表单重复提交的方法
本文实例讲述了PHP使用token防止表单重复提交的方法.分享给大家供大家参考,具体如下: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2 ...
- token防止表单重复提交
出现表单重复提交的三种情况: 一.服务器响应缓慢,用户多次点击提交按钮. 二.提交成功后刷新页面. 三.提交成功后返回表单页面再次点击提交. package com.jalja.token; impo ...
- java struts2入门学习--防止表单重复提交.OGNL语言学习
一.知识点回顾 防止表单重复提交核心思想: 客户端和服务器端和写一个token,比较两个token的值相同,则非重复提交;不同,则是重复提交. 1.getSession三种方式比较: request. ...
- struts2 自带的 token防止表单重复提交拦截器
在struts2中,我们可以利用struts2自带的token拦截器轻松实现防止表单重复提交功能! 1. 在相应的action配置中增加: <interceptor-ref name=&quo ...
随机推荐
- 【C# .Net GC】GC初始化设置 和GcSetting
相关的类 GcSetting 类 GCLargeObjectHeapCompactionMode 枚举 GCLargeObjectHeapCompactionMode 枚举 属性的值 GCSettin ...
- WPF中常用控件(TreeView, ComboBox, DataGrid, ListView)使用MVVM模式绑定的demo
之前几篇关于TreeView的博客中只是贴了源代码,并没有把整个项目上传到github.最近就想着把我常用的几个控件做成一个demo,这样也方便自己以后查看.本人也是WPF新手,但是我并没有打算就往这 ...
- linux – 使用find -name命令的通配符和双引号
转至:https://www.icode9.com/content-3-209958.html 双引号(和单引号)是否抑制了通配符扩展?如果是这样,那么为什么以下呢? find -name " ...
- Android studio常用快捷键导包的设置
下面是一些快捷键的使用还有快速导包的设置 1. Ctrl+G 同时按下Ctrl+G快捷键弹出快速定位框,在框中输入行数点击OK即可快速切换到对应的行数,如图2.17所示. 2. Ctrl+E 同时按下 ...
- Qt:QNetworkReply
0.说明 QNetworkReply对象包含了Manager发送的请求头和返回的数据. 它继承自QIODevice,所以可以用各种read获取其中返回的数据: QByteArray data = re ...
- Cache一致性与DMA
cache一致性与DMA 第一个问题 对于进行DMA操作的设备, 并不是所有系统都保持它们的cache一致性.在这种情况下, 准备进行DMA的设备可能从RAM得到陈旧的数据, 因为脏的cache行可能 ...
- ASP.NET Core 6框架揭秘实例演示[17]:利用IHttpClientFactory工厂来创建HttpClient
在一个采用依赖注入框架的应用中,我们一般不太推荐利用手工创建的HttpClient对象来进行HTTP调用,使用的HttpClient对象最好利用注入的IHttpClientFactory工厂来创建.前 ...
- CSAPP-Lab03 Attack Lab 记录
纸上得来终觉浅,绝知此事要躬行 实验概览 Attack!成为一名黑客不正是我小时候的梦想吗?这个实验一定会很有趣. CMU 对本实验的官方说明文档:http://csapp.cs.cmu.edu/3e ...
- angular批量上传图片并进行校验
最近做一个项目,有一个批量上传图片,并校验是否是图片和上传最大数量的需求.下面分享一下我的想法,欢迎大神探讨. 首先是在html中支持批量上传和接收图片(mac下设置了之后不存在类型校验),下面上ht ...
- 明火烟雾目标检测项目部署(YoloV5+Flask)
明火烟雾目标检测项目部署 目录 明火烟雾目标检测项目部署 1. 拉取Docker PyToch镜像 2. 配置项目环境 2.1 更换软件源 2.2 下载vim 2.3 解决vim中文乱码问题 3. 运 ...