javaweb防重复提交的几种方式

本文针对javaweb项目中防止重复提交几种方式做简单整理。

重复提交场景:

  1. 点击提交按钮两次。
  2. 点击刷新按钮。
  3. 使用浏览器后退按钮重复之前的操作,导致重复提交表单。
  4. 使用浏览器历史记录重复提交表单。
  5. 浏览器重复的 HTTP 请求。
  6. 多人同时时间对同一笔单据进行操作。

方式一:通过JavaScript屏蔽提交按钮

通过js代码,当用户点击提交按钮后,屏蔽提交按钮使用户无法点击提交按钮或点击无效,从而实现防止表单重复提交。

ps:js代码很容易被绕过。比如用户通过刷新页面方式,或使用postman等工具绕过前段页面仍能重复提交表单。因此不推荐此方法。

javaweb防重复提交的几种方式_第1张图片

 

方式二:利用Session防止表单重复提交

服务器返回表单页面时,会先生成一个Token保存于session,并把该Toen传给表单页面。当表单提交时会带上Token,服务器拦截器Interceptor会拦截该请求,拦截器判断session保存的Token和表单提交Token是否一致。若不一致或session的Token为空或表单未携带Token则不通过。首次提交表单时session的Token与表单携带的Token一致走正常流程,然后拦截器内会删除session保存的Token。当再次提交表单时由于session的Token为空则不通过。从而实现了防止表单重复提交。

实现:

  • 在配置文件spring-context.xml中添加已经编写完成的拦截器,可以对所有的请求进行拦截,也可以对某个请求进行拦截,
  • 编写Token拦截器。方法的主要实现逻辑为:先判断接口方法存储的是新建注解Tokensave方法还是remove方法,若为save方法,将生成一个标识码存储到session中放回到前端页面进行存储。若为remove方法,会将前端返回的标识码与服务器存储的标识码进行比对,若2个标识码一致,则通过。若服务器或者前端返回的标识码有一方为空或者2个标识码不一致,则证明存在重复提交,则跳转到相应的提示界面并提示用户存在重复提交的信息
  • 例如:

	
        
        
            
            
        
        
        
        	
        	
        	
        	
            
        
	
	
public class TokenInterceptor extends HandlerInterceptorAdapter {  
    private static final Logger LOG = Logger.getLogger(TokenInterceptor.class);  
    @Override  
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  
        if (handler instanceof HandlerMethod) {  
            HandlerMethod handlerMethod = (HandlerMethod) handler;  
            Method method = handlerMethod.getMethod();  
            Token annotation = method.getAnnotation(Token.class);  
            if (annotation != null) {  
                boolean needSaveSession = annotation.save();  
                if (needSaveSession) {  
                    request.getSession(true).setAttribute("token", UUID.randomUUID().toString());  
                }  
                boolean needRemoveSession = annotation.remove();  
                if (needRemoveSession) {  
                    if (isRepeatSubmit(request)) {  
                        LOG.warn("please don't repeat submit,url:"+ request.getServletPath());  
                        throw new BusinessException("无效请求,请刷新页面后重试!");
                    }  
                    request.getSession(true).removeAttribute("token");  
                }  
            }  
            return true;  
        } else {  
            return super.preHandle(request, response, handler);  
        }  
    }  
  
    private boolean isRepeatSubmit(HttpServletRequest request) {  
        String serverToken = (String) request.getSession(true).getAttribute("token");  
        if (serverToken == null) {  
            return true;  
        }  
        String clinetToken = request.getParameter("token");  
        if (clinetToken == null) {  
            return true;  
        }  
        if (!serverToken.equals(clinetToken)) {  
            return true;  
        }  
        return false;  
    }  
} 

 

相应的控制层controller方法添加Token注解,方法上方添加@Token(save=true)后,服务器会生成唯一的标识码并将该标识码传递给前端保存。方法上方添加@Token(remove=true)后,服务器会把之前存储的唯一标识进行删除,以防止重复提交时,前端传递的唯一标识服务器此时已经删除,校验相同不通过以达到反正重复提交的目的。

@RequestMapping("/ckcmdjPage")
@Token(save=true)
	public ModelAndView ckcmdjPage(String djnm) {
		Map model = new HashMap();
		Map paraMap = new HashMap();
		paraMap.put("djnm", djnm);
		Spckd spckd = cpldjglService.querySpckdList(paraMap);
		model.put("data", spckd);
		//进入页面并显示相应信息
		return new ModelAndView("genersoft/crkgl/cplgl/cpldjgl/ckcmdjPage", model);
	}
@RequestMapping("/ckqrcmAction")
@Token(remove=true)
	public String ckqrcmAction(String djnm,HttpServletRequest req) {
		cpldjglService.deleteCrklcCk(djnm);
		req.setAttribute("message", "成功");
		//返回登记管理主页面
		return "genersoft/crkgl/cplgl/cpldjgl/djglquery";
	}

页面设置token值,在前端的jsp页面或者html页面添加一个隐藏的input来保存服务器返回的唯一标识,页面进行保存若为form表单提交需要将页面的token值放入到隐藏域中。若使用的是ajax请求,需要将token值放入到参数中传递给服务器

方式三:前台业务校验

前台提交时查询下当前单据状态,根据不同状态进行判断拦截。如下图所示

javaweb防重复提交的几种方式_第2张图片

方式四:数据库锁

悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。往往依靠数据库提供的锁机制。在数据库中,悲观锁的流程如下:

在对记录进行修改前,先尝试为该记录加上排他锁(exclusive locking)。

如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常。具体响应方式由开发者根据实际需要决定。

如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。

其间如果有其他事务对该记录做加锁的操作,都要等待当前事务解锁或直接抛出异常。简单例子如下所示

javaweb防重复提交的几种方式_第3张图片

乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。主要代码实现如下所示

方式五:明细页面禁止右键刷新

document.oncontextmenu = function () { return false; }

 

你可能感兴趣的:(JAVA,java,javascript,mysql)