<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
具體的做法:
1、獲取使用者填寫使用者名稱和密碼的頁面時向後臺傳送一次請求,這時後臺會生成唯一的隨機標識號,專業術語稱為Token(令牌)。
2、將Token傳送到使用者端的Form表單中,在Form表單中使用隱藏域來儲存這個Token,表單提交的時候連同這個Token一起提交到伺服器端。
3、伺服器端判斷使用者端提交上來的Token與伺服器端生成的Token是否一致,如果不一致,那就是重複提交了,此時伺服器端就可以不處理重複提交的表單。如果相同則處理表單提交,處理完後清除當前使用者的Session域中儲存的標識號。
看具體的範例:
1.建立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() ;//建立令牌 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); } }
推薦,非常簡單,頁面不需要任何傳入,只需要在驗證的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>
主要是利用了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, "操作失敗!", "執行防止重複提交功能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){ //加鎖成功 //執行方法 Object funcResult = joinPoint.proceed(); //方法執行完之後進行解鎖 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上鎖。如果連續點選提交,則提示不能重複提交,當上鎖的那次操作執行完,redis釋放了鎖之後才能繼續提交。
以上就是java後臺防止表單重複提交方法詳解的詳細內容,更多關於java後臺防止表單重複提交的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45