JavaWeb中HttpSession中表单的重复提交示例

作者:袖梨 2022-06-29

表单的重复提交

  • 重复提交的情况:

①. 在表单提交到一个 Servlet,而 Servlet 又通过请求转发的方式响应了一个 JSP(HTML)页面,此时地址栏还保留着 Servlet 的那个路径,在响应页面点击 “刷新”。

②. 在响应页面没有到达时,重复点击 “提交按钮”

③. 点击返回,再点击提交

  • 不是重复提交的情况:点击 “返回”,“刷新” 原表单页面,再点击提交。

  • 如何避免表单的重复提交:在表单中做一个标记,提交到 Servlet 时,检查标记是否存在且和预定义的标记一样,若一致,则受理请求,并销毁标记,若不一致或没有标记,则直接响应提示信息:“重复提交”

①仅提供一个隐藏域不行:

②把标记放在 Request 中 , 行不通,表单页面刷新后,request 已经被销毁,再提交表单是一个新的 request 的。

③把标记放在 Session 中,可以

1. 在原表单页面,生成一个随机值 token
2. 在原表单页面,把 token 值放入 session 属性中

3. 在原表单页面,把 token 值放入到隐藏域

4. 在目标的 Servlet 中:获取 session 和隐藏域中的 token 值

比较两个值是否一致,受理请求,且把 session 域中的 token 属性清除,若不一致,则直接响应提示页面:“重复提交”

我们可以通过 Struts1 中写好的类 TokenProcessor 来重构代码, 面向组件编程

代码如下 复制代码

packagecom.lsy.javaweb;

importjavax.servlet.http.HttpServletRequest;

importjavax.servlet.http.HttpSession;

importjava.security.MessageDigest;

importjava.security.NoSuchAlgorithmException;

publicclassTokenProcessor {

privatestaticfinalString TOKEN_KEY ="TOKEN_KEY";

privatestaticfinalString TRANSACTION_TOKEN_KEY ="TRANSACTION_TOKEN_KEY";

/**

* The singleton instance of this class.

*/

privatestaticTokenProcessor instance =newTokenProcessor();

/**

* The timestamp used most recently to generate a token value.

*/

privatelongprevious;

/**

* Protected constructor for TokenProcessor. Use

* TokenProcessor.getInstance() to obtain a reference to the processor.

*/

protectedTokenProcessor() {

super();

}

/**

* Retrieves the singleton instance of this class.

*/

publicstaticTokenProcessor getInstance() {

returninstance;

}

/**

*


* Return true 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 false under any of the

* following circumstances:

*

*

*

    *

    *

    • No session associated with this request

    *

    *

    • No transaction token saved in the session

    *

    *

    • No transaction token included as a request parameter

    *

    *

    • The included transaction token value does not match the transaction

    * token in the user's session

    *

    *

    *

    * @param request

    * The servlet request we are processing

    */

    publicsynchronizedbooleanisTokenValid(HttpServletRequest request) {

    returnthis.isTokenValid(request,false);

    }

    /**

    * Return true 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 false

    *

    *

      *

      *

      • No session associated with this request

      *

      • No transaction token saved in the session

      *

      *

      • No transaction token included as a request parameter

      *

      *

      • The included transaction token value does not match the transaction

      * token in the user's session

      *

      *

      *

      * @param request

      * The servlet request we are processing

      * @param reset

      * Should we reset the token after checking it?

      */

      publicsynchronizedbooleanisTokenValid(HttpServletRequest request,booleanreset) {

      // Retrieve the current session for this request

      HttpSession session = request.getSession(false);

      if(session ==null) {

      returnfalse;

      }

      // Retrieve the transaction token from this session, and

      // reset it if requested

      String saved = (String) session.getAttribute(TRANSACTION_TOKEN_KEY);

      if(saved ==null) {

      returnfalse;

      }

      if(reset) {

      this.resetToken(request);

      }

      // Retrieve the transaction token included in this request

      String token = request.getParameter(TOKEN_KEY);

      if(token ==null) {

      returnfalse;

      }

      returnsaved.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

      */

      publicsynchronizedvoidresetToken(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

      */

      publicsynchronizedString saveToken(HttpServletRequest request) {

      HttpSession session = request.getSession();

      String token = generateToken(request);

      if(token !=null) {

      session.setAttribute(TRANSACTION_TOKEN_KEY, token);

      }

      returntoken;

      }

      /**

      * Generate a new transaction token, to be used for enforcing a single

      * request for a particular transaction.

      *

      * @param request

      * The request we are processing

      */

      publicsynchronizedString generateToken(HttpServletRequest request) {

      HttpSession session = request.getSession();

      returngenerateToken(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.

      */

      publicsynchronizedString generateToken(String id) {

      try{

      longcurrent = System.currentTimeMillis();

      if(current == previous) {

      current++;

      }

      previous = current;

      byte[] now =newLong(current).toString().getBytes();

      MessageDigest md = MessageDigest.getInstance("MD5");

      md.update(id.getBytes());

      md.update(now);

      returntoHex(md.digest());

      }catch(NoSuchAlgorithmException e) {

      returnnull;

      }

      }

      /**

      * Convert a byte array to a String of hexadecimal digits and return it.

      *

      * @param buffer

      * The byte array to be converted

      */

      privateString toHex(byte[] buffer) {

      StringBuffer sb =newStringBuffer(buffer.length *2);

      for(inti =0; i

      sb.append(Character.forDigit((buffer[i] &0xf0) >>4,16));

      sb.append(Character.forDigit(buffer[i] &0x0f,16));

      }

      returnsb.toString();

      }

      }

      相关文章

      精彩推荐