JavaWeb中HttpSession中表單的重復提交示例
表單的重復提交
- 重復提交的情況:
①. 在表單提交到一個 Servlet,而 Servlet 又通過請求轉(zhuǎn)發(fā)的方式響應了一個 JSP(HTML)頁面,此時地址欄還保留著 Servlet 的那個路徑,在響應頁面點擊 “刷新”。
②. 在響應頁面沒有到達時,重復點擊 “提交按鈕”
③. 點擊返回,再點擊提交
- 不是重復提交的情況:點擊 “返回”,“刷新” 原表單頁面,再點擊提交。
- 如何避免表單的重復提交:在表單中做一個標記,提交到 Servlet 時,檢查標記是否存在且和預定義的標記一樣,若一致,則受理請求,并銷毀標記,若不一致或沒有標記,則直接響應提示信息:“重復提交”
①僅提供一個隱藏域不行:<input type="hidden" name="token" value="lsy">
②把標記放在 Request 中 , 行不通,表單頁面刷新后,request 已經(jīng)被銷毀,再提交表單是一個新的 request 的。
③把標記放在 Session 中,可以
1. 在原表單頁面,生成一個隨機值 token
2. 在原表單頁面,把 token 值放入 session 屬性中
3. 在原表單頁面,把 token 值放入到隱藏域
4. 在目標的 Servlet 中:獲取 session 和隱藏域中的 token 值
比較兩個值是否一致,受理請求,且把 session 域中的 token 屬性清除,若不一致,則直接響應提示頁面:“重復提交”
我們可以通過 Struts1 中寫好的類 TokenProcessor 來重構(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中表單的重復提交示例,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
SpringCloud Alibaba微服務實戰(zhàn)之遠程Feign請求頭丟失問題解決方案
這篇文章主要介紹了SpringCloud Alibaba微服務實戰(zhàn)之遠程Feign請求頭丟失問題,對SpringCloud Alibaba Feign請求頭問題感興趣的朋友跟隨小編一起看看吧2024-02-02SpringBoot項目中jar發(fā)布獲取jar包所在目錄路徑的最佳方法
在開發(fā)過程中,我們經(jīng)常要遇到上傳圖片、word、pdf等功能,但是當我們把項目打包發(fā)布到服務器上時,對應的很多存儲路徑的方法就會失效,下面這篇文章主要給大家介紹了關(guān)于SpringBoot項目中jar發(fā)布獲取jar包所在目錄路徑的相關(guān)資料2022-07-07SpringBoot @ExceptionHandler與@ControllerAdvice異常處理詳解
在Spring Boot應用的開發(fā)中,不管是對底層數(shù)據(jù)庫操作,對業(yè)務層操作,還是對控制層操作,都會不可避免的遇到各種可預知的,不可預知的異常需要處理,如果每個處理過程都單獨處理異常,那么系統(tǒng)的代碼耦合度會很高,工作量大且不好統(tǒng)一,以后維護的工作量也很大2022-10-10關(guān)于String轉(zhuǎn)Json的幾種方式
這篇文章主要介紹了關(guān)于String轉(zhuǎn)Json的幾種方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12JSON反序列化Long變Integer或Double的問題及解決
這篇文章主要介紹了JSON反序列化Long變Integer或Double的問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01Maven實戰(zhàn)之搭建Maven私服和鏡像的方法(圖文)
本篇文章主要介紹了搭建Maven私服和鏡像的方法(圖文),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12