注册 登录  
 加关注
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

流星永恒的博客

JSF,Facelets,Rich(Prime)Faces,和java的笔记

 
 
 

日志

 
 

Preventing Double-Form Submit (JSF)  

2009-12-31 15:29:41|  分类: jsf2.0 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

There are already out of the box solutions for this thing, like synchronizer token in seam or shale's s:token. But if you are not using these frameworks & wanna implement your own, here is what I've done. Actually it's just a phaseListener and an extended formRenderer. The phaseListener gets the submitted uniqueToken. If there is one, it checks for if it's in the visitedTokenList. If it is, response gets rendered with a warning message. If there is none, it puts the submittedToken into visitedTokenList stored in session. Then the formRenderer gets the generated uniqueToken from session and renders it to the page.

 

The PL also makes a check over the request whether it's an ajaxRequest or not. I implemented the check mechanism for Ajax4JSF and AjaxAnywhere. The reason for storing the tokens in a map is to also support the framed pages.

 

 

MultipleFormSubmitPreventionPhaseListener.java

 

package tr.dev.pl;

import java.util.Date;

import java.util.Map;

import javax.faces.application.FacesMessage;

import javax.faces.context.FacesContext;

import javax.faces.event.PhaseEvent;

import javax.faces.event.PhaseId;

import javax.faces.event.PhaseListener;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

public class MultipleFormSubmitPreventionPhaseListener implements PhaseListener {

  private static final Log logger = LogFactory.getLog(MultipleFormSubmitPreventionPhaseListener.class);

  private static final String WARNING_MESSAGE = new String("Dude, don't mess with the browser!");

  

  private static final String KEYVAL_AJAX4JSF_REQUEST     = "AJAXREQUEST";

  private static final String KEYVAL_AJAXANYWHERE_REQUEST   = "aaxmlrequest";

  

  public PhaseId getPhaseId() {

    return PhaseId.RESTORE_VIEW;

  }

  

  public void beforePhase(PhaseEvent phaseEvent) {

    // No-op

  }

  

  public void afterPhase(PhaseEvent phaseEvent) {

    FacesContext facesContext = phaseEvent.getFacesContext();

        if (isRequestedByAjax4JSF(facesContext)) {

          if (logger.isDebugEnabled())

            logger.debug("Request is an Ajax4JSF Request. Multiple-submit-check will not be done.");

        }

        else if (isRequestedByAjaxAnywhere(facesContext)) {

          if (logger.isDebugEnabled())

            logger.debug("Request is an AjaxAnywhere Request. Multiple-submit-check will not be done.");

        }

        else  {

          checkForDoubleSubmit(facesContext);

        }

  }

  private String getRequestURI() {

    HttpServletRequest servletRequest = (HttpServletRequest) FacesContext.getCurrentInstance()

                                                                               .getExternalContext().getRequest();

    return servletRequest.getRequestURI().substring(0, servletRequest.getRequestURI().lastIndexOf("."));

  }

    

  private boolean isRequestedByAjax4JSF(FacesContext facesContext) {

    String ajaxReqVal = (String) facesContext.getExternalContext().getRequestParameterMap()

                                                                    

.get(KEYVAL_AJAX4JSF_REQUEST);

    if (StringUtils.isNotEmpty(ajaxReqVal))

      return true;

    return false;

  }

  

  private boolean isRequestedByAjaxAnywhere(FacesContext facesContext) {

    Map requestHeaderMap = facesContext.getExternalContext().getRequestHeaderMap();

    

    String ajaxAnywhereReq = (String) requestHeaderMap.get(KEYVAL_AJAXANYWHERE_REQUEST);

    if ("true".equals(ajaxAnywhereReq)) {

      return true;

    }

    return false;

  }

  

  private void checkForDoubleSubmit(FacesContext facesContext) {

    Map requestParamMap = facesContext.getExternalContext().getRequestParameterMap();

    String submittedUniqueToken = (String) requestParamMap.get("uniqueToken");

    if (!StringUtils.isEmpty(submittedUniqueToken)) {

      if (logger.isDebugEnabled())

        logger.debug("submitted UniqueToken: " + submittedUniqueToken);

      

      if (!isInVisitedTokenList(facesContext, submittedUniqueToken)) {

        if (logger.isWarnEnabled())

          logger.warn("Submitted token is not in visited token list. Preventing 

                                  multiple submit. Rendering response with warn message.");        

        

        FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_FATAL, WARNING_MESSAGE, WARNING_MESSAGE);

        facesContext.addMessage(StringUtils.EMPTY, msg);

        

        facesContext.renderResponse();

      }

      else {

        getVisitedTokenMap(facesContext).remove(getRequestURI());

      }

    }

    String newUniqueToken = getUniqueToken(facesContext);

      

    if (logger.isDebugEnabled())

      logger.debug("adding token: " + newUniqueToken + " to visited token list");

      

    getVisitedTokenMap(facesContext).put(getRequestURI(), newUniqueToken);

  }

  private String getUniqueToken(FacesContext facesContext) {

    HttpServletRequest request = (HttpServletRequest) facesContext.getExternalContext().getRequest();

    return request.getSession().getId() + (new Date()).getTime();

  }

  private boolean isInVisitedTokenList(FacesContext context, String submittedUniqueToken) {

    return getVisitedTokenMap(context).containsValue(submittedUniqueToken);

  }

  private Map getVisitedTokenMap(FacesContext context) {

    return (Map) context.getApplication().createValueBinding("#{visitedTokenMap}").getValue(context);

  }

}

 

MyFormRenderer.java

 

package tr.dev.renderer;

import java.io.IOException;

import java.util.Map;

import javax.faces.component.UIComponent;

import javax.faces.context.FacesContext;

import javax.faces.context.ResponseWriter;

import javax.servlet.http.HttpServletRequest;

import com.sun.faces.renderkit.html_basic.FormRenderer;

public class MyFormRenderer extends FormRenderer {

  

  public void encodeBegin(FacesContext context, UIComponent component) throws IOException {

    ResponseWriter writer = context.getResponseWriter();

    

    super.encodeBegin(context, component);

    renderUniqueToken(writer, component);

  }

  

  private String getRequestURI() {

    HttpServletRequest servletRequest = (HttpServletRequest) FacesContext.getCurrentInstance()

                                                                          .getExternalContext().getRequest();

    return servletRequest.getRequestURI().substring(0, servletRequest.getRequestURI().lastIndexOf("."));

  }

  

  private void renderUniqueToken(ResponseWriter writer, UIComponent component) throws IOException {

    writer.startElement("input", component);

    writer.writeAttribute("type", "hidden", "type");

    writer.writeAttribute("name", "uniqueToken", "name");

    String uniqueToken = getUniqueTokenFromSession(FacesContext.getCurrentInstance());

    writer.writeAttribute("value", uniqueToken == null ? "" : uniqueToken, "value");

    writer.endElement("input");

  }

  

  private String getUniqueTokenFromSession(FacesContext facesContext) {

    String token = null;

    Map tokenMap = (Map) facesContext.getApplication().createValueBinding("#{visitedTokenMap}")

                                                                            .getValue(facesContext);

    

    if (tokenMap != null)

      token = (String) tokenMap.get(getRequestURI());

    

    return token;

  }

}

 

faces-config.xml

 

<lifecycle>

      <phase-listener>tr.dev.pl.MultipleFormSubmitPreventionPhaseListener</phase-listener>

</lifecycle>

 

<managed-bean>

      <managed-bean-name>visitedTokenMap</managed-bean-name>

      <managed-bean-class>java.util.HashMap</managed-bean-class>

      <managed-bean-scope>session</managed-bean-scope>

</managed-bean>  

 

<render-kit>

      <renderer>

            <component-family>javax.faces.Form</component-family>

            <renderer-type>javax.faces.Form</renderer-type>

            <renderer-class>

                  tr.dev.renderer.MyFormRenderer

            </renderer-class>

      </renderer>

</render-kit>

  评论这张
 
阅读(1101)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018