JavaWeb中HttpSession中表單的重復(fù)提交示例
表單的重復(fù)提交
- 重復(fù)提交的情況:
①. 在表單提交到一個(gè) Servlet,而 Servlet 又通過(guò)請(qǐng)求轉(zhuǎn)發(fā)的方式響應(yīng)了一個(gè) JSP(HTML)頁(yè)面,此時(shí)地址欄還保留著 Servlet 的那個(gè)路徑,在響應(yīng)頁(yè)面點(diǎn)擊 “刷新”。
②. 在響應(yīng)頁(yè)面沒(méi)有到達(dá)時(shí),重復(fù)點(diǎn)擊 “提交按鈕”
③. 點(diǎn)擊返回,再點(diǎn)擊提交
- 不是重復(fù)提交的情況:點(diǎn)擊 “返回”,“刷新” 原表單頁(yè)面,再點(diǎn)擊提交。
- 如何避免表單的重復(fù)提交:在表單中做一個(gè)標(biāo)記,提交到 Servlet 時(shí),檢查標(biāo)記是否存在且和預(yù)定義的標(biāo)記一樣,若一致,則受理請(qǐng)求,并銷(xiāo)毀標(biāo)記,若不一致或沒(méi)有標(biāo)記,則直接響應(yīng)提示信息:“重復(fù)提交”
①僅提供一個(gè)隱藏域不行:<input type="hidden" name="token" value="lsy">
②把標(biāo)記放在 Request 中 , 行不通,表單頁(yè)面刷新后,request 已經(jīng)被銷(xiāo)毀,再提交表單是一個(gè)新的 request 的。
③把標(biāo)記放在 Session 中,可以
1. 在原表單頁(yè)面,生成一個(gè)隨機(jī)值 token
2. 在原表單頁(yè)面,把 token 值放入 session 屬性中
3. 在原表單頁(yè)面,把 token 值放入到隱藏域
4. 在目標(biāo)的 Servlet 中:獲取 session 和隱藏域中的 token 值
比較兩個(gè)值是否一致,受理請(qǐng)求,且把 session 域中的 token 屬性清除,若不一致,則直接響應(yīng)提示頁(yè)面:“重復(fù)提交”
我們可以通過(guò) Struts1 中寫(xiě)好的類(lèi) TokenProcessor 來(lái)重構(gòu)代碼, 面向組件編程
package com.lsy.javaweb; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class TokenProcessor { private static final String TOKEN_KEY = "TOKEN_KEY"; private static final String TRANSACTION_TOKEN_KEY = "TRANSACTION_TOKEN_KEY"; /** * The singleton instance of this class. */ private static TokenProcessor instance = new TokenProcessor(); /** * The timestamp used most recently to generate a token value. */ private long previous; /** * Protected constructor for TokenProcessor. Use * TokenProcessor.getInstance() to obtain a reference to the processor. */ protected TokenProcessor() { super(); } /** * Retrieves the singleton instance of this class. */ public static TokenProcessor getInstance() { return instance; } /** * <p> * Return <code>true</code> if there is a transaction token stored in the * user's current session, and the value submitted as a request parameter * with this action matches it. Returns <code>false</code> under any of the * following circumstances: * </p> * * <ul> * * <li>No session associated with this request</li> * * <li>No transaction token saved in the session</li> * * <li>No transaction token included as a request parameter</li> * * <li>The included transaction token value does not match the transaction * token in the user's session</li> * * </ul> * * @param request * The servlet request we are processing */ public synchronized boolean isTokenValid(HttpServletRequest request) { return this.isTokenValid(request, false); } /** * Return <code>true</code> if there is a transaction token stored in the * user's current session, and the value submitted as a request parameter * with this action matches it. Returns <code>false</code> * * <ul> * * <li>No session associated with this request</li> * <li>No transaction token saved in the session</li> * * <li>No transaction token included as a request parameter</li> * * <li>The included transaction token value does not match the transaction * token in the user's session</li> * * </ul> * * @param request * The servlet request we are processing * @param reset * Should we reset the token after checking it? */ public synchronized boolean isTokenValid(HttpServletRequest request, boolean reset) { // Retrieve the current session for this request HttpSession session = request.getSession(false); if (session == null) { return false; } // Retrieve the transaction token from this session, and // reset it if requested String saved = (String) session.getAttribute(TRANSACTION_TOKEN_KEY); if (saved == null) { return false; } if (reset) { this.resetToken(request); } // Retrieve the transaction token included in this request String token = request.getParameter(TOKEN_KEY); if (token == null) { return false; } return saved.equals(token); } /** * Reset the saved transaction token in the user's session. This indicates * that transactional token checking will not be needed on the next request * that is submitted. * * @param request * The servlet request we are processing */ public synchronized void resetToken(HttpServletRequest request) { HttpSession session = request.getSession(false); if (session == null) { return; } session.removeAttribute(TRANSACTION_TOKEN_KEY); } /** * Save a new transaction token in the user's current session, creating a * new session if necessary. * * @param request * The servlet request we are processing */ public synchronized String saveToken(HttpServletRequest request) { HttpSession session = request.getSession(); String token = generateToken(request); if (token != null) { session.setAttribute(TRANSACTION_TOKEN_KEY, token); } return token; } /** * Generate a new transaction token, to be used for enforcing a single * request for a particular transaction. * * @param request * The request we are processing */ public synchronized String generateToken(HttpServletRequest request) { HttpSession session = request.getSession(); return generateToken(session.getId()); } /** * Generate a new transaction token, to be used for enforcing a single * request for a particular transaction. * * @param id * a unique Identifier for the session or other context in which * this token is to be used. */ public synchronized String generateToken(String id) { try { long current = System.currentTimeMillis(); if (current == previous) { current++; } previous = current; byte[] now = new Long(current).toString().getBytes(); MessageDigest md = MessageDigest.getInstance("MD5"); md.update(id.getBytes()); md.update(now); return toHex(md.digest()); } catch (NoSuchAlgorithmException e) { return null; } } /** * Convert a byte array to a String of hexadecimal digits and return it. * * @param buffer * The byte array to be converted */ private String toHex(byte[] buffer) { StringBuffer sb = new StringBuffer(buffer.length * 2); for (int i = 0; i < buffer.length; i++) { sb.append(Character.forDigit((buffer[i] & 0xf0) >> 4, 16)); sb.append(Character.forDigit(buffer[i] & 0x0f, 16)); } return sb.toString(); } }
以上所述是小編給大家介紹的JavaWeb中HttpSession中表單的重復(fù)提交示例,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
SpringCloud Alibaba微服務(wù)實(shí)戰(zhàn)之遠(yuǎn)程Feign請(qǐng)求頭丟失問(wèn)題解決方案
這篇文章主要介紹了SpringCloud Alibaba微服務(wù)實(shí)戰(zhàn)之遠(yuǎn)程Feign請(qǐng)求頭丟失問(wèn)題,對(duì)SpringCloud Alibaba Feign請(qǐng)求頭問(wèn)題感興趣的朋友跟隨小編一起看看吧2024-02-02SpringBoot項(xiàng)目中jar發(fā)布獲取jar包所在目錄路徑的最佳方法
在開(kāi)發(fā)過(guò)程中,我們經(jīng)常要遇到上傳圖片、word、pdf等功能,但是當(dāng)我們把項(xiàng)目打包發(fā)布到服務(wù)器上時(shí),對(duì)應(yīng)的很多存儲(chǔ)路徑的方法就會(huì)失效,下面這篇文章主要給大家介紹了關(guān)于SpringBoot項(xiàng)目中jar發(fā)布獲取jar包所在目錄路徑的相關(guān)資料2022-07-07SpringBoot @ExceptionHandler與@ControllerAdvice異常處理詳解
在Spring Boot應(yīng)用的開(kāi)發(fā)中,不管是對(duì)底層數(shù)據(jù)庫(kù)操作,對(duì)業(yè)務(wù)層操作,還是對(duì)控制層操作,都會(huì)不可避免的遇到各種可預(yù)知的,不可預(yù)知的異常需要處理,如果每個(gè)處理過(guò)程都單獨(dú)處理異常,那么系統(tǒng)的代碼耦合度會(huì)很高,工作量大且不好統(tǒng)一,以后維護(hù)的工作量也很大2022-10-10Java實(shí)現(xiàn)文件變化監(jiān)聽(tīng)代碼實(shí)例
這篇文章主要介紹了Java實(shí)現(xiàn)文件變化監(jiān)聽(tīng)代碼實(shí)例,通過(guò)定時(shí)任務(wù),輪訓(xùn)查詢(xún)文件的最后修改時(shí)間,與上一次進(jìn)行對(duì)比,如果發(fā)生變化,則說(shuō)明文件已經(jīng)修改,進(jìn)行重新加載或?qū)?yīng)的業(yè)務(wù)邏輯處理,需要的朋友可以參考下2024-01-01關(guān)于String轉(zhuǎn)Json的幾種方式
這篇文章主要介紹了關(guān)于String轉(zhuǎn)Json的幾種方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12JSON反序列化Long變Integer或Double的問(wèn)題及解決
這篇文章主要介紹了JSON反序列化Long變Integer或Double的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01Maven實(shí)戰(zhàn)之搭建Maven私服和鏡像的方法(圖文)
本篇文章主要介紹了搭建Maven私服和鏡像的方法(圖文),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-12-12