一、使用方法
1 、 假如你要提交的页面为toSubmit.jsp ;
2 、 在打开toSubmit.jsp 的Action1 中加入:saveToken(request) ,例如
public ActionForward execute( ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 生成同步令牌 saveToken(request);
return mapping.findForward("toSubmit"); } |
3 、 在提交toSubmit.jsp 的Action2 中加入:isTokenValid(request, true) ,例如:
public ActionForward execute( ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // 验证同步令牌 if (isTokenValid(request, true)) { // 执行提交操作 }else { // 重复提交 return mapping.findForward("Error"); } } |
4 、 使用注意:toSubmit.jsp 中的form 必须使用struts 的标签<html:form> 。
二、基本原理
第一步、在session 中放入同步令牌
在Action1 中加入了saveToken(request) 的方法后,调用TokenProcessor 类的saveToken 方法如下:
public synchronized void saveToken(HttpServletRequest request) { HttpSession session = request.getSession(); String token = generateToken(request); if (token != null) { session.setAttribute(Globals.TRANSACTION_TOKEN_KEY, token); } } |
很明显在session 中放入了同步令牌,名称为Globals.TRANSACTION_TOKEN_KEY 。
第二步、在页面创建hidden 元素
当应用服务器初始化toSubmit.jsp 页面遇到标签<html:form> 时,便会调用struts 的FormTag 类,其中有一个方法:
protected String renderToken() { StringBuffer results = new StringBuffer(); HttpSession session = pageContext.getSession(); if (session != null) { String token = (String) session.getAttribute(Globals.TRANSACTION_TOKEN_KEY); if (token != null) { results.append("<input type=\"hidden\" name=\""); results.append(Constants.TOKEN_KEY); results.append("\" value=\""); results.append(token); if (this.isXhtml()) { results.append("\" />"); } else { results.append("\">"); } } } return results.toString(); } |
其意为:当检测到session 中的Globals.TRANSACTION_TOKEN_KEY 不为空时,在toSubmit.jsp 页面创建元素:
<input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value=""> |
名称为:org.apache.struts.taglib.html.TOKEN 就是Constants.TOKEN_KEY ;
值为:session 中的Globals.TRANSACTION_TOKEN_KEY 的值,即为同步令牌值。
第三步、验证同步令牌
在Action2 中加入isTokenValid 方法,实际上是调用TokenProcessor 类的isTokenValid 方法如下:
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(Globals.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(Constants.TOKEN_KEY); if (token == null) { return false; } return saved.equals(token); } |
它首先取得session 中的令牌值,然后resetToken ,再从页面hidden 元素取来令牌值,进行比较,如果相等则为第一次,不等则为重复提交。
其中resetToken 方法如下:
public synchronized void resetToken(HttpServletRequest request) { HttpSession session = request.getSession(false); if (session == null) { return; } session.removeAttribute(Globals.TRANSACTION_TOKEN_KEY); |