若是表单能够重复提交,会给服务器带来一些没必要要的麻烦。也许,您会说,只要咱们把提交的Action隐藏就能够了。例如在Struts中,只须要在Forward的后面加入一个属性redirect="true"就能够了。这个办法虽然看不见那个*.do了,可是别人可使用后退,等退到刚才的页面,继续点提交,因此这方法不可行。
不过刚才在网上还看了一下资料,网上说有三中方法。
第一个是在javascipt中设置一个变量。 javascript
第二个是在第一次提交完成,给提交按钮加属性disabled html
注意:这个disabled属性值在JS中应该是disabled或者" ",而在C#中,值才是true或者false,不要搞浑了哦!~
你们想一想,这可都是javascript,能够说是在当前页面有效的。若是我提交之后,跳到别的页面,而后我在后退回来,页面仍是要从新加载,那么咱们的变量不就白设置。难道说这两种方法就不行了吗?不能这样说,既然人家写出来确定有人家的道理,只不过咱们用错地方了。您想一下,什么提交会在一个页面完成?对AJAX,因此说,这两个方法,只能应用杂AJAX提交中。 java
真正能解决问题的,固然就是咱们今天的重点--->Token(令牌)
在Struts中,咱们只须要调用几个方法,就能够完成。先来看看我是如何作的,而后我说下原理。
先来作一个Action(TokenAction),在这个Action中生成一个令牌。 apache
而后跳转到test4.jsp页面,执行咱们的提交操做。 服务器
提交进入LoginAction,在这个Action中作判断。 session
到这里,咱们的提交问题就算解决了。怎么样,很简单吧。毕竟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);
}
所谓知其然,还要知其因此然,这样才能来去自如嘛,嘿嘿!~ this