Token(令牌)防止表单重复提交

如果表单可以重复提交,会给服务器带来一些不必要的麻烦。也许,您会说,只要我们把提交的Action隐藏就可以了。例如在Struts中,只需要在Forward的后面加入一个属性redirect="true"就可以了。这个办法虽然看不见那个*.do了,但是别人可以使用后退,等退到刚才的页面,继续点提交,所以这方法不可行。
不过刚才在网上还看了一下资料,网上说有三中方法。
第一个是在javascipt中设置一个变量。



第二个是在第一次提交完成,给提交按钮加属性disabled



注意:这个disabled属性值在JS中应该是disabled或者" ",而在C#中,值才是true或者false,不要搞浑了哦!~
大家想想,这可都是javascript,可以说是在当前页面有效的。如果我提交以后,跳到别的页面,然后我在后退回来,页面还是要重新加载,那么我们的变量不就白设置。难道说这两种方法就不行了吗?不能这样说,既然人家写出来肯定有人家的道理,只不过我们用错地方了。您想一下,什么提交会在一个页面完成?对AJAX,所以说,这两个方法,只能应用杂AJAX提交中。


真正能解决问题的,当然就是我们今天的重点--->Token(令牌)
在Struts中,我们只需要调用几个方法,就可以完成。先来看看我是如何做的,然后我说下原理。
先来做一个Action(TokenAction),在这个Action中生成一个令牌。



然后跳转到test4.jsp页面,执行我们的提交操作。



提交进入LoginAction,在这个Action中做判断。



这样就提交成功了。这很正常。



现在我们点后退,在提交一下。


到这里,我们的提交问题就算解决了。怎么样,很简单吧。毕竟Struts已经给写好了,我们只需要调用就可以了。
不过需要注意的一点,在提交的时候,要用<html:form>,如果用一般的From标签,就跳不到提交成功页面(到底是为什么,马上就会讲到!)
看到这里,您会问了,我们刚才就调用了,那几个方法,可是内部怎样实现的我们一点都不知道啊!~别急,等我慢慢说来。
Step 1:
先来看看第一个方法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
Step 2:
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();
}
刚才,还记得,我在上面说到一个注意,说是要用<html:from>原因就在这第二步。因为当应用服务器初始化test4.jsp页面遇到标签<html:form>时,便会调用struts的FormTag类的renderToken()方法。
第二步的意思是:当检测到session中的Globals.TRANSACTION_TOKEN_KEY不为空时,在test4.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的值,即为同步令牌值。
step 3:
取得session中的令牌值,然后resetToken,再从页面hidden元素取来令牌值,进行比较,如果相等则为第一次,不等则为重复提交。
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);
}
删除同步令牌。
public synchronized void resetToken(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session == null) {
      return;
}
session.removeAttribute(Globals.TRANSACTION_TOKEN_KEY);
}
所谓知其然,还要知其所以然,这样才能来去自如嘛,嘿嘿!~


转载于:https://my.oschina.net/weiweiblog/blog/618429

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值