使用拦截器和redis+token实现防重复提交完整代码
文章目录
- redis配置:
- 自定义一个注解:
- 自定义类继承HandlerInterceptor
- mvc添加刚刚自定义的拦截器使之生效
- tokenservice
- controller
redis配置:
# redis
spring.redis.host=47.101.210.219
spring.redis.port=6379
spring.redis.timeout=0
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
其实也可以用jedis连接redis服务器,与使用redistemplate可以起到相同效果。
redistemplate需要配置序列化,以防出现乱码:
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
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.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;/*** @Author h* @Description redis配置类* @Date 11.17**/
@Configuration
public class redisConfig {@Bean//1.项目启动时此方法先被注册成bean被spring管理public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();// 配置连接工厂template.setConnectionFactory(factory);//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和publicom.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常//om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);jacksonSeial.setObjectMapper(om);// 值采用json序列化template.setValueSerializer(jacksonSeial);//使用StringRedisSerializer来序列化和反序列化redis的key值template.setKeySerializer(new StringRedisSerializer());// 设置hash key 和value序列化模式template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(jacksonSeial);template.afterPropertiesSet();return template;}
}
用于操作键值的工具:
/*** @Author hzy* @Description redis工具类* @Date 11.17**/
@Component
public class RedisTemplateUtil {@Qualifier("redisTemplate")@Autowiredprivate RedisTemplate redisTemplate;/*** 写入缓存* @param key* @param value* @return*/public boolean set(final String key, Object value) {boolean result = false;try{ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();operations.set(key, value);result = true;} catch(Exception e) {e.printStackTrace();}return result;}/*** 写入缓存设置失效时间* @param key* @param value* @param expireTime* @return*/public boolean setEx(final String key, Object value, Long expireTime) {boolean result = false;try{ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();operations.set(key, value);redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);result = true;} catch(Exception e) {e.printStackTrace();}return result;}/*** 判断缓存中是否有对应的value* @param key* @return*/public boolean exists(final String key) {return redisTemplate.hasKey(key);}/*** 读取缓存* @param key* @return*/public Object get(final String key) {Object result = null;ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();result = operations.get(key);return result;}/*** 删除对应的value* @param key*/public boolean remove(final String key) {if(exists(key)) {Boolean delete= redisTemplate.delete(key);return delete;}return false;}
}
自定义一个注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ForbidSubmit {}
自定义类继承HandlerInterceptor
关于HandlerInterceptor:
允许自定义处理程序执行链的工作流接口。 应用程序可以为某些处理程序组注册任意数量的现有或自定义拦截器,以添加常见的预处理行为,而无需修改每个处理程序实现。
HandlerInterceptor 在适当的 HandlerAdapter 触发处理程序本身的执行之前被调用。 这种机制可用于预处理方面的大量领域,例如授权检查或常见的处理程序行为,如区域设置或主题更改。 它的主要目的是允许分解出重复的处理程序代码。
在异步处理场景中,处理程序可能在单独的线程中执行,而主线程退出而不呈现或调用postHandle和afterCompletion回调。 当并发处理程序执行完成时,请求被分派回来以继续渲染模型,并再次调用此合约的所有方法。 有关更多选项和详细信息,请参阅org.springframework.web.servlet.AsyncHandlerInterceptor
通常每个 HandlerMapping bean 定义一个拦截器链,共享它的粒度。 为了能够将某个拦截器链应用于一组处理程序,需要通过一个 HandlerMapping bean 映射所需的处理程序。 拦截器本身被定义为应用程序上下文中的 bean,映射 bean 定义通过其“拦截器”属性(在 XML 中: 的 )引用。
HandlerInterceptor 基本上类似于 Servlet 过滤器,但与后者相反,它只允许自定义预处理和禁止执行处理程序本身的选项,以及自定义后处理。 过滤器更强大,例如它们允许交换传递给链的请求和响应对象。 请注意,过滤器在 web.xml 中配置,即应用程序上下文中的 HandlerInterceptor。
作为基本准则,与细粒度处理程序相关的预处理任务是 HandlerInterceptor 实现的候选对象,尤其是分解出的公共处理程序代码和授权检查。 另一方面,过滤器非常适合请求内容和视图内容处理,例如多部分表单和 GZIP 压缩。 这通常显示何时需要将过滤器映射到某些内容类型(例如图像)或所有请求。
定义拦截器:
/*** @Author h* @Description 防止重复提交拦截器* @Date 11.17**/public class RepeatInterceptor implements HandlerInterceptor {@Autowired
private TokenService tokenService;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {//HandlerMethod:封装有关由方法和bean组成的处理程序方法的信息。// 提供对方法参数、方法返回值、方法注解等的便捷访问。//类可以使用 bean 实例或 bean 名称(例如,lazy-init bean、prototype bean)创建。if(handler instanceof HandlerMethod)
{HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();//被ForbidSubmit注释的方法ForbidSubmit annotation = method.getAnnotation(ForbidSubmit.class);
if(annotation != null) {try {tokenService.checkToken(request,method.getName());tokenService.createToken(response,method.getName());}catch (Exception e){Result result = Result.fail("不允许重复提交,请稍后重试");ServletUtils.renderString(response, JSONUtils.marshal(result));throw e;}}//renderstring方法: response.setContentType("application/json");// response.setCharacterEncoding("utf-8");// response.getWriter().print(string);return true;
}else {return true;//return preHandle(request,response,handler);}}}
mvc添加刚刚自定义的拦截器使之生效
@Configuration
@EnableTransactionManagement
public class MyConfig extends WebMvcConfigurationSupport {@Autowired
private RepeatInterceptor repeatInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry)
{registry.addInterceptor(repeatInterceptor);super.addInterceptors(registry);//添加重复提交的拦截器
}
}
tokenservice
/*** @Author h* @Description 用于创建token, 在防止重复提交时使用到* @Date 11.17**/
@Service
public class TokenService {String TOKEN_NAME = "token";String TOKEN_PREFIX = "token:";// 过期时间, 10s,Integer EXPIRE_TIME_MINUTE = 10;// 过期时间, 一小时Integer EXPIRE_TIME_HOUR = 60 * 60;// 过期时间, 一天Integer EXPIRE_TIME_DAY = 60 * 60 * 24;//token引用了redis服务,创建token采用随机算法工具类生成随机uuid字符串,然后放入到redis中// (为了防止数据的冗余保留,这里设置过期时间为xx秒,具体可视业务而定),如果放入成功,最后返回这个token值。// checkToken方法就是从redis中获取token到值(如果拿不到就添加一个键值对),// 如若不存在,直接抛出异常。这个异常信息可以被拦截器捕捉到,然后返回给前端。@Autowiredprivate RedisTemplateUtil redisTemplateUtil;public String createToken(HttpServletResponse response,String name){String str = UUID.randomUUID().toString();StringBuilder token = new StringBuilder();response.setHeader("Access-Control-Expose-Headers",TOKEN_NAME);try {token.append(TOKEN_PREFIX).append(str);String key = TOKEN_PREFIX+ IpUtils.getHostIp()+name;response.setHeader(TOKEN_NAME,token.toString());if(redisTemplateUtil.exists(key)){return redisTemplateUtil.get(key).toString();}redisTemplateUtil.setEx(key, token.toString(), EXPIRE_TIME_MINUTE.longValue());boolean notEmpty = Objects.nonNull(token.toString());if(notEmpty){return token.toString();}}catch (Exception e){e.printStackTrace();}return new String();}public boolean checkToken(HttpServletRequest request,String name) throws Exception{String key = TOKEN_PREFIX+IpUtils.getHostIp()+name;boolean exists = redisTemplateUtil.exists(key);if(exists){System.out.println("已经存在");throw new CustomException(400L, "请勿重复提交");}return true;}
}
自己在拦截器中先检查redis库是否有相应的键,如果有则直接返回禁止提交的信息给前端
controller
/*** 新增保存角色信息*/@ForbidSubmit@ApiOperation("新增一个角色")@PostMapping("/add/{RoleName}")@ResponseBodypublic Result addSave(@PathVariable("RoleName") String newRoleName,HttpServletResponse response){InowRole inowRole = new InowRole();inowRole.setRoleName(newRoleName);inowRole.setCreateBy("ShiroUtils.getLoginName()");//获得登陆者的姓名inowRole.setCreateTime(new Date());inowRole.setDelFlag(0);if(inowRoleService.save(inowRole)){return Result.success(inowRole);}else{return Result.fail("新增失败");}}
在上面的controller方法中加了 @ForbidSubmit注解,这样访问时可以起到拦截作用。
测试:
第一次访问接口
第二次访问时已经弹出了异常信息:
后记:
aop也可以起到拦截器相同的功能,且细粒度更大,只需要定义一个防止重复提交的aspect即可,以后有时间再试试。
使用拦截器和redis+token实现防重复提交完整代码相关推荐
- 拦截器中实现防止表单重复提交
最近项目中又出现重复数据,除了id不同,其他的完全相同的一样的数据,导致业务关系应该唯一不唯一 问题产生的原因:以下订单举例说明,因为各种原因(网络卡,快递点击等)重复提交2个或者以上一模一样的订单, ...
- 给axios的请求拦截器中配置token
axios 1. 配置token 用户第一次登录系统时,服务器端会返回一个身份秘钥信息(token),表明当前用户有资格.权限访问服务器,客户端获取token之后,通过sessionStorage存储 ...
- 表单防重复提交拦截器
表单防重复提交 http://blog.icoolxue.com/submitted-by-spring-mvc-to-prevent-data-duplication/
- redis+aop防重复提交
文章目录 1.防重复提交注解 2.redis分布式锁 3.防止重复提交Aop 之前有记录一篇用redis+拦截器防重复提交的内容: redis+拦截器防重复提交 1.防重复提交注解 @Target(E ...
- Java的token解决方案,SpringMVC后台token防重复提交解决方案
思路1.添加拦截器,拦截需要防重复提交的请求 2.通过注解@Token来添加token/移除token 3.前端页面表单添加(如果是Ajax请求则需要在请求的json数据中添加token值) 核 ...
- AOP+自定义注解token令牌和参数防重复提交实战
目录 一.哪些因素会引起重复提交? 二.重复提交会带来哪些问题? 三.订单的防重复提交你能想到几种方案? 四.自定义注解方式 4.1Java核心知识-自定义注解(先了解下什么是自定义注解) 4.1.1 ...
- 架构设计 | 接口幂等性原则,防重复提交Token管理
本文源码:GitHub·点这里 || GitEE·点这里 一.幂等性概念 1.幂等简介 编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同.就是说,一次和多次请求某一个资源会产 ...
- 处理接口超时_架构设计 | 接口幂等性原则,防重复提交Token管理
一.幂等性概念 1.幂等简介 编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同.就是说,一次和多次请求某一个资源会产生同样的作用影响. 2.HTTP请求 遵循Http协议的请 ...
- springBoot防重复提交
一.重复提交原因 由于客户端抖动,人为快速点击,造成服务器重复处理 二.后端防重复提交 1.基于token 访问请求到达服务器,服务器端生成token,分别保存在客户端和服务器.提交请求到达服务器,服 ...
最新文章
- 利用CNN来检测伪造图像
- jdbc 获取mysql表注释_jdbc读取数据库,表相关信息(含注释)
- MongoDB安装步骤
- Minikube的启动日志
- mysql 5.7 windows 主从_windows server下Mysql5.7主从复制
- 403 forbidden_[SpringSecurity] 自定义403页面
- 动态规划0—1背包问题
- python ddos攻击脚本_python版本DDOS攻击脚本
- 如何在java输入_怎么在java中编写输入语句?
- 利用MVC思想和php语言写网站的心得
- 3dmax学习6——扫描命令
- 【python 走进NLP】机器学习和深度学习情感分类模型
- 【Android】关于ARouter转场动画的问题
- 2020 年 Github 上最牛的 Java 进阶教程及 Java 实战项目都在这里了!
- MongoDB find查询语句详解
- Java总结之基础篇
- 1.1 css style 样式定义:行内 style 属性、单页 <style> 标签、多页 <style> 标签
- SSM+智能家居产品销售管理 毕业设计-附源码161618
- Pycharm 常用快捷键大全【快查字典版】
- 从程序员辞职到知乎上市,你还以为周源的成功是偶然吗?
热门文章
- Java格式化日期和时间模式占位符
- HTML字体怎么显示,教你如何用CSS来控制网页字体的显示样式
- python去掉列表的括号和逗号_在每行之后显示没有逗号,括号等的python 2d...
- map集合遍历_java---map集合获取元素与存储元素
- 机器学习算法之——隐马尔可夫模型(Hidden Markov Models,HMM) 代码实现
- 股票系列,动态规划,加油,九月太浪了,十月不许浪
- 六十六,完成SpringBoot项目中的员工增删查改功能
- 如何应用AutoML加速图机器学习任务的处理?
- @请注意查收:《史上最全的AI论文资料》.pdf
- 贝叶斯神经网络对梯度攻击的鲁棒性