控制添加器(使用拦截器防止表单重复提交)

Posted

篇首语:男儿欲遂平生志,五经勤向窗前读。本文由小常识网(cha138.com)小编为大家整理,主要介绍了控制添加器(使用拦截器防止表单重复提交)相关的知识,希望对你有一定的参考价值。

控制添加器(使用拦截器防止表单重复提交)

业务场景介绍

web系统经常会出现用户在页面上快速点击多次提交按钮(或者重复刷新页面),在后台会连续接收多次请求,除了第一次外,其他的相同请求就是重复提交。

如何避免页面重复提交呢,正常有以下几种方法:

  1. 前台控制:点击后,使用js将按钮事件移除
  2. 后台控制:生成token存储session中,使用页面拦截器校验
  3. 后台控制:生成token放入redis中并设置有效期,让其自动失效

方法1过于简单暴力,有一定效果;方法2有点复杂也不是很灵活,其实和方法2有点类似

这里要详细介绍的是方法2,通过拦截器+注解,使用简单灵活。

使用页面拦截器校验token,防止重复提交

基本原理

  1. 通过拦截器拦截页面请求
  2. 在打开页面时生成token,并存储在session中
  3. 页面上提交时将token传到后台,在拦截器中校验token
  4. 如果token不匹配则拒绝请求,并返回错误信息
  5. 如果token匹配,则删除或重新生成token

详细代码

1、添加springMvc请求拦截器AvoidDuplicateSubmitInterceptor.java

/** * 页面重复提交拦截器 *  * @author huangjian * */public class AvoidDuplicateSubmitInterceptor extends HandlerInterceptorAdapter     private final Logger logger = LoggerFactory.getLogger(getClass());    @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)            throws Exception         if (handler instanceof HandlerMethod)             HandlerMethod handlerMethod = (HandlerMethod) handler;            Method method = handlerMethod.getMethod();            SubmitToken annotation = method.getAnnotation(SubmitToken.class);            if (annotation == null)                 return true;                        HttpSession session = request.getSession(false);            if (annotation.value().equals(TokenType.create))                 SessionTokenUtil.createToken(session);                return true;                        if (isRepeatSubmit(request))                 logger.warn("页面重复提交,url:", request.getRequestURI());                RenderUtil.renderFailure("页面重复提交", response);                return false;                        if (annotation.value().equals(TokenType.refresh))                 // 验证成功后,重新生成token                SessionTokenUtil.createToken(session);             else                 // 验证成功后,删除token                SessionTokenUtil.removeToken(session);                            return true;        /** 判断是否重复提交 */    private boolean isRepeatSubmit(HttpServletRequest request)         String serverToken = SessionTokenUtil.getToken(request.getSession(false));        if (serverToken == null)             return true;                String clientToken = request.getParameter(SessionTokenUtil.TOKEN_KEY);        if (clientToken == null)             return true;                if (!serverToken.equals(clientToken))             return true;                return false;    

2、添加工具类SessionTokenUtil.java

public class SessionTokenUtil     public static final String TOKEN_KEY = "submitToken";    /** 生成10位随机数作为token */    private static String createToken()         return RandomUtils.generateString(10);        public static void createToken(HttpSession session)         session.setAttribute(SessionTokenUtil.TOKEN_KEY, createToken());        public static void removeToken(HttpSession session)         session.removeAttribute(SessionTokenUtil.TOKEN_KEY);        public static String getToken(HttpSession session)         String serverToken = (String) session.getAttribute(TOKEN_KEY);        return serverToken;    

3、添加注解SubmitToken.java,以及枚举TokenType.java

@Retention(RetentionPolicy.RUNTIME)@Target( METHOD )public @interface SubmitToken     TokenType value() default TokenType.create;
public enum TokenType     create, remove, refresh

4、在controller中使用

1)在打开页面方法标记生成token(方法仅是示例)

/** 进入模块 详细/新增 页 */@SubmitToken(TokenType.create)@RequestMapping(value = "detail/id", method = RequestMethod.GET)public String detail(@PathVariable("id") Long id, Model model, HttpServletRequest request) 

2)在提交保存方法标记刷新或删除token(方法仅是示例)

@SubmitToken(TokenType.refresh)@RequestMapping(value = "save", method = RequestMethod.POST)public void save(@Valid @ModelAttribute("preload") BsCx entity, HttpServletRequest request,        HttpServletResponse response)     try         //TODO 执行保存操作...        // 将新的token返回给页面,并在页面刷新token        String serverToken = SessionTokenUtil.getToken(request.getSession(false));        RenderUtil.renderSuccess(serverToken, response);     catch (Exception e)         //    

3)在页面上面添加token值,如果需要支持连续操作,要在保存成功后,更新submitToken值

<input type="hidden" id="submitToken" name="submitToken" th:value="$session.submitToken" />

上面是thymeleaf模板示例代码

5、配置拦截器

下面是springboot配置示例

@Configurationpublic class WebAppConfig implements WebMvcConfigurer     @Override    public void addInterceptors(InterceptorRegistry registry)         registry.addInterceptor(new AvoidDuplicateSubmitInterceptor());    

相关参考

广告报站器(福州推出文明公交“随手拍”,每提交一次都能抽奖)

...公交“扫一扫”扫描公交车上二维码进入“随手拍”通过提交相关图片或视频,反馈公交车辆、公交

气体防止器(选购制氧机要注意哪些问题?上海市质标院提示)

...通过变压吸附(PSA)原理,以空气作为原材料,不需要任何添加剂,接通电源后吸附空气中的氮气及其他气体,从空气中分离出纯度较高的氧气(9

排污橡胶止回阀(给排水专业小知识:河北广宗县自来水污染,谈倒流防止器和止回阀)

...和同行交流,最大限度提升环保从业专业技能!(此处已添加圈子卡片,请到今日头条客户端查看)本期主题:给排水专业中,止回阀和倒流防止器,两者的区别,你清楚吗?近日河北广宗

旋流防止器安装示意图(消防水系统与防排烟系统)

...置的固定灭火设备,是建筑物中最基本的设施。主要作用控制可燃物、隔绝助燃物、消除着火源。通过供消防车从市政给水管网或室外消防给水管网取水实施灭火,也可以直接连接水带、水枪出水灭火。所以,消火栓系统也是扑...

控制按扭(【Activiti工作流升级】流程按钮控制、待阅、审批意见、流程跳过)

一、流程按钮控制1、流程设计新增节点设置-按钮权限2、流程审批节点驳回:可以驳回到已经经过的流程节点。任务委派:比如经理将任务委派给部门员工进行操作,操作完成后继续由经理继续审批给下一节点。任务转派:将任...

开表单(想把表单设计做到极致,居然有这么多细节需要注意)

编辑导语:表单设计老生常谈,但又是使用频繁且经常遇到各种问题的一种组件,尤其在B端业务中。本文详细分享了在表单设计中我们应该注意的细节,一起来看看吧!我们每天都在填写各种表单,例如发帖、填单、登录等等...

怎么关掉手机语音提示(拦截境外来电,这招管用)

...有关规定境外电话进入我国通信网络时必须在主叫号码前添加国际冠字“00”(部分手机将“00”翻译为“+”)且严禁在传送过程中删除该冠字如您没有接收境外来电的需求请主动关闭该功能降低被骗风险守护财产安全如何拦截...

怎么提醒自己出门带手机(@所有人,拦截境外来电,这招管用)

...有关规定境外电话进入我国通信网络时必须在主叫号码前添加国际冠字“00”(部分手机将“00”翻译为“+”)且严禁在传送过程中删除该冠字如您没有接收境外来电的需求请主动关闭该功能降低被骗风险守护财产安全如何拦截...

常用的容器类控件有哪些(Flutter基础控件介绍)

...t(文本),Image(图片),xxButton(按钮),TextField(输入框),Form(表单)Text(\'HelloWorld\');Form单独说一下Form组件 实际业务中,在正式向服务器提交数据前,都会对各个输入框数据进行合法性校验,但是对每一个Tex

新颖的团购名称(“群里填表太烦”全新接龙表单出炉,这样填表才爽)

...而已。作为大家的得力帮手,文档君赶紧带着超强全新「表单接龙」功能赶来啦!实时掌握填写进度,是否填写一目了然多类型题目+丰富模板,满足