防止表单的重复提交【利用Struts1的TokenProcessor源码】

1)修改Struts的源码

⑴首先从官网下载Struts1的源码

struts-1.3.10-src
我下载的是struts-1.3.10-src。

⑵找到 TokenProcessor 这个类

TokenProcessor类
路径 :
struts-1.3.10-src\src\core\src\main\java\org\apache\struts\util

⑶修改源码

6个错误
一共有6处错误。

这6处错误都很好修改:
修改错误
①修改包名
②去掉一个多余引用
③添加2个常量 :

private static final String TOKEN_KEY = "TOKEN_KEY";
private static final String TRANSACTION_TOKEN_KEY = "TRANSACTION_TOKEN_KEY";

④修改 saveToken 方法
修改前
找到 saveToken 方法。
修改返回值为 String,然后 return token; 即可。

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;
}



这是修改后的完整类 (自己修改包名):
package com.test.servlet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
* TokenProcessor is responsible for handling all token related functionality.
* The methods in this class are synchronized to protect token processing from
* multiple threads. Servlet containers are allowed to return a different
* HttpSession object for two threads accessing the same session so it is not
* possible to synchronize on the session.
*
* @since Struts 1.1
*/

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

}

2)编写jsp页面

jsp页面
页面很简单:一个表单。里面一个隐藏域,一个输入框,一个提交按钮。
页面

值得注意的是:
在导包的时候可能会出现下面的情况:
导包错误
这样可能下次再打开工程会报错,我们只要加一个空格即可。
导包正确

3)编写Servlet

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    boolean valid = TokenProcessor.getInstance().isTokenValid(request);

    if (valid) {
        // 移除随机生成的 TOKEN_KEY
        TokenProcessor.getInstance().resetToken(request);
    } else {
        // 要重定向的 “重复提交” 的提示页面
        response.sendRedirect(request.getContextPath() + "/...");
        return;
    }

    String nameStr = request.getParameter("name");
    System.out.println(nameStr);
    // 要转发的 “提交成功” 的提示页面
    request.getRequestDispatcher("/...").forward(request, response);
}

编写doPost方法,即可。

随机生成的 TOKEN_KEY
随机生成的 TOKEN_KEY

4)Tips

在修改 TokenProcessor 类时,添加的 2 个常量的其中一个:
    private static final String TOKEN_KEY = “TOKEN_KEY”;
这个 TOKEN_KEY 所赋的值 与我们编写的 jsp 页面中的 隐藏域的 name 的值 要一致。
    input type=”hidden”name=”TOKEN_KEY”
             value=”%=TokenProcessor.getInstance().saveToken(request)%>”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值