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();

}

}

相关文章

精彩推荐