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

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());    

相关参考