java后臺防止表單重復提交方法詳解
方案一:利用Session防止表單重復提交
具體的做法:
1、獲取用戶填寫用戶名和密碼的頁面時向后臺發(fā)送一次請求,這時后臺會生成唯一的隨機標識號,專業(yè)術語稱為Token(令牌)。
2、將Token發(fā)送到客戶端的Form表單中,在Form表單中使用隱藏域來存儲這個Token,表單提交的時候連同這個Token一起提交到服務器端。
3、服務器端判斷客戶端提交上來的Token與服務器端生成的Token是否一致,如果不一致,那就是重復提交了,此時服務器端就可以不處理重復提交的表單。如果相同則處理表單提交,處理完后清除當前用戶的Session域中存儲的標識號。
看具體的范例:
1.創(chuàng)建FormServlet,用于生成Token(令牌)和跳轉到form.jsp頁面
import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class FormServlet extends HttpServlet { private static final long serialVersionUID = -884689940866074733L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String token = UUID.randomUUID().toString() ;//創(chuàng)建令牌 System.out.println("在FormServlet中生成的token:"+token); request.getSession().setAttribute("token", token); //在服務器使用session保存token(令牌) request.getRequestDispatcher("/form.jsp").forward(request, response);//跳轉到form.jsp頁面 } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
2.在form.jsp中使用隱藏域來存儲Token(令牌)
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>form表單</title> </head> <body> <form action="${pageContext.request.contextPath}/servlet/DoFormServlet" method="post"> <%--使用隱藏域存儲生成的token--%> <%-- <input type="hidden" name="token" value="<%=session.getAttribute("token") %>"> --%> <%--使用EL表達式取出存儲在session中的token--%> <input type="hidden" name="token" value="${token}"/> 用戶名:<input type="text" name="username"> <input type="submit" value="提交"> </form> </body> </html>
3.DoFormServlet處理表單提交
import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class DoFormServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { boolean b = isRepeatSubmit(request);//判斷用戶是否是重復提交 if(b==true){ System.out.println("請不要重復提交"); return; } request.getSession().removeAttribute("token");//移除session中的token System.out.println("處理用戶提交請求??!"); } /** * 判斷客戶端提交上來的令牌和服務器端生成的令牌是否一致 * @param request * @return * true 用戶重復提交了表單 * false 用戶沒有重復提交表單 */ private boolean isRepeatSubmit(HttpServletRequest request) { String client_token = request.getParameter("token"); //1、如果用戶提交的表單數據中沒有token,則用戶是重復提交了表單 if(client_token==null){ return true; } //取出存儲在Session中的token String server_token = (String) request.getSession().getAttribute("token"); //2、如果當前用戶的Session中不存在Token(令牌),則用戶是重復提交了表單 if(server_token==null){ return true; } //3、存儲在Session中的Token(令牌)與表單提交的Token(令牌)不同,則用戶是重復提交了表單 if(!client_token.equals(server_token)){ return true; } return false; } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
方案二:判斷請求url和數據是否和上一次相同
推薦,非常簡單,頁面不需要任何傳入,只需要在驗證的controller方法上寫上自定義注解即可
1.寫好自定義注解
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 一個用戶 相同url 同時提交 相同數據 驗證 * @author Administrator * */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface SameUrlData { }
2.寫好攔截器
import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import com.thinkgem.jeesite.common.mapper.JsonMapper; /** * 一個用戶 相同url 同時提交 相同數據 驗證 * 主要通過 session中保存到的url 和 請求參數。如果和上次相同,則是重復提交表單 * @author Administrator * */ public class SameUrlDataInterceptor extends HandlerInterceptorAdapter{ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); SameUrlData annotation = method.getAnnotation(SameUrlData.class); if (annotation != null) { if(repeatDataValidator(request))//如果重復相同數據 return false; else return true; } return true; } else { return super.preHandle(request, response, handler); } } /** * 驗證同一個url數據是否相同提交 ,相同返回true * @param httpServletRequest * @return */ public boolean repeatDataValidator(HttpServletRequest httpServletRequest) { String params=JsonMapper.toJsonString(httpServletRequest.getParameterMap()); String url=httpServletRequest.getRequestURI(); Map<String,String> map=new HashMap<String,String>(); map.put(url, params); String nowUrlParams=map.toString();// Object preUrlParams=httpServletRequest.getSession().getAttribute("repeatData"); if(preUrlParams==null)//如果上一個數據為null,表示還沒有訪問頁面 { httpServletRequest.getSession().setAttribute("repeatData", nowUrlParams); return false; } else//否則,已經訪問過頁面 { if(preUrlParams.toString().equals(nowUrlParams))//如果上次url+數據和本次url+數據相同,則表示城府添加數據 { return true; } else//如果上次 url+數據 和本次url加數據不同,則不是重復提交 { httpServletRequest.getSession().setAttribute("repeatData", nowUrlParams); return false; } } } } <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="*.*.SameUrlDataInterceptor"/> </mvc:interceptor>
方案三:利用Spring AOP和redis的鎖來實現(xiàn)防止表單重復提交
主要是利用了redis的分布式鎖機制
1、注解:
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 防止重復提交注解 * @author zzp 2018.03.11 * @version 1.0 */ @Retention(RetentionPolicy.RUNTIME) // 在運行時可以獲取 @Target(value = {ElementType.METHOD, ElementType.TYPE}) // 作用到類,方法,接口上等 public @interface PreventRepetitionAnnotation { }
2、AOP代碼
import java.lang.reflect.Method; import java.util.UUID; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.com.rlid.utils.json.JsonBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.stereotype.Component; import demo.zzp.app.aop.annotation.OperaterAnnotation; import demo.zzp.app.redis.JedisUtils; /** * 防止重復提交操作AOP類 * @author zzp 2018.03.10 * @version 1.0 */ @Aspect @Component @EnableAspectJAutoProxy(proxyTargetClass=true) public class PreventRepetitionAspect { @Autowired private JedisUtils jedisUtils; private static final String PARAM_TOKEN = "token"; private static final String PARAM_TOKEN_FLAG = "tokenFlag"; /** * around * @throws Throwable */ @Around(value = "@annotation(demo.zzp.app.aop.annotation.PreventRepetitionAnnotation)") public Object excute(ProceedingJoinPoint joinPoint) throws Throwable{ try { Object result = null; Object[] args = joinPoint.getArgs(); for(int i = 0;i < args.length;i++){ if(args[i] != null && args[i] instanceof HttpServletRequest){ HttpServletRequest request = (HttpServletRequest) args[i];//被調用的方法需要加上HttpServletRequest request這個參數 HttpSession session = request.getSession(); if(request.getMethod().equalsIgnoreCase("get")){ //方法為get result = generate(joinPoint, request, session, PARAM_TOKEN_FLAG); }else{ //方法為post result = validation(joinPoint, request, session, PARAM_TOKEN_FLAG); } } } return result; } catch (Exception e) { e.printStackTrace(); return JsonBuilder.toJson(false, "操作失敗!", "執(zhí)行防止重復提交功能AOP失敗,原因:" + e.getMessage()); } } public Object generate(ProceedingJoinPoint joinPoint, HttpServletRequest request, HttpSession session,String tokenFlag) throws Throwable { String uuid = UUID.randomUUID().toString(); request.setAttribute(PARAM_TOKEN, uuid); return joinPoint.proceed(); } public Object validation(ProceedingJoinPoint joinPoint, HttpServletRequest request, HttpSession session,String tokenFlag) throws Throwable { String requestFlag = request.getParameter(PARAM_TOKEN); //redis加鎖 boolean lock = jedisUtils.tryGetDistributedLock(tokenFlag + requestFlag, requestFlag, 60000); if(lock){ //加鎖成功 //執(zhí)行方法 Object funcResult = joinPoint.proceed(); //方法執(zhí)行完之后進行解鎖 jedisUtils.releaseDistributedLock(tokenFlag + requestFlag, requestFlag); return funcResult; }else{ //鎖已存在 return JsonBuilder.toJson(false, "不能重復提交!", null); } } }
3、Controller代碼
@RequestMapping(value = "/index",method = RequestMethod.GET) @PreventRepetitionAnnotation public String toIndex(HttpServletRequest request,Map<String, Object> map){ return "form"; } @RequestMapping(value = "/add",method = RequestMethod.POST) @ResponseBody @PreventRepetitionAnnotation public String add(HttpServletRequest request){ try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } return JsonBuilder.toJson(true, "保存成功!",null); }
第一次點擊提交表單,判斷到當前的token還沒有上鎖,即給該token上鎖。如果連續(xù)點擊提交,則提示不能重復提交,當上鎖的那次操作執(zhí)行完,redis釋放了鎖之后才能繼續(xù)提交。
以上就是java后臺防止表單重復提交方法詳解的詳細內容,更多關于java后臺防止表單重復提交的資料請關注腳本之家其它相關文章!
相關文章
SpringBoot靜態(tài)資源CSS等修改后再運行無效的解決
這篇文章主要介紹了SpringBoot靜態(tài)資源CSS等修改后再運行無效的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12解決idea創(chuàng)建版本時只有Java21和Java17選項
你是否在使用IntelliJ?IDEA創(chuàng)建新項目時遇到了只有Java?21和Java?17的選項?別擔心,我們的指南將為你提供解決方案,通過簡單的步驟,你將能夠選擇你需要的任何Java版本,繼續(xù)閱讀,讓我們開始吧!2024-03-03springboot中使用FastJson解決long類型在js中失去精度的問題
這篇文章主要介紹了springboot中使用FastJson解決long類型在js中失去精度的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06k8s部署springboot實現(xiàn)前后端分離項目
本文主要介紹了k8s部署springboot實現(xiàn)前后端分離項目,主要包括配置文件、鏡像構建和容器編排等方面,具有一定的參考價值,感興趣的可以了解一下2024-01-01