任何项目里面都要处理这个问题, 我觉得客户端和服务端都做处理才合适.
客户端这边, 一般情况下, 浏览器都会缓存已经访问过的页面, 在浏览器第一次请求时, 返回的响应消息中的Last-Modified头字段可用于指定响应内容的最后更新时间,当客户机缓存此文档内容后,它在以后的请求消息中将根据Last-Modified头字段指定的时间来生成If-Modified-Since请求头字段,以指出缓存文档的最后更新时间。只有文档的修改时间比If-Modified-Since请求头指定的时间新时,服务器才会返回文档内容。如果自从If-Modified-Since指定的时间以来,网页内容没有发生修改,服务器将返回一个304(Not Modified)状态码来表示浏览器缓存的版本是最新的,而不会向浏览器返回文档内容,浏览器则继续使用以前缓存的内容。通过这种方式,可以在一定程度上减少浏览器与服务器之间的通信数据量,从而提高了通信效率。
说简单点就是要清除缓存. 就三句代码
<%
response.setHeader("Expires","0");
response.setHeader("Cache-Control","no-store");
response.setHeader("Pragrma","no-cache");
%>
服务器端, 若项目用到了struts, 那么struts的令牌就是很好的解决办法. 在提交的jsp以及该jsp的前后action中这样处理, 提交前的action中
saveToken(request);
提交后的action中 if (isTokenValid(request, true)) {
//执行提交操作
}else {
// 重复提交
}
就这几句代码就行, 但由前action到jsp 和jsp到后action显然是两次对话, 用request是怎么判断的, 这是我刚接触令牌的时候就不解的问题, 后来考虑想必是在jsp页面上做了处理, 保存了这个相关的变量又传入后action的, 像struts早有鸡肋一说, 也许某天就会退出这个舞台, 也许以后得用jsp/servlet自己写框架, 研究了下令牌机制的源代码.
在org.apache.struts.action.action中
protected void saveToken(HttpServletRequest request) {
token.saveToken(request);
}
这里的token是org.apache.struts.util.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);
}
}
这里的Globals.TRANSACTION_TOKEN_KEY是一个常量"org.apache.struts.action.TOKEN"
这里的generateToken(request)是token的另一方法
public synchronized String generateToken(HttpServletRequest request) {
HttpSession session = request.getSession();
return generateToken(session.getId());
}
而在generateToken(session.getId())这个方法中主要是对id进行了加密处理
然后把这个加密后的字符串放入session中.
在jsp页面中则必须使用struts的form标签, 因为在调用标签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不为空时,在jsp页面创建元素
<input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="">
这个可以查看生成的jsp页面的html代码, 会发行紧跟着form标签下就是这个hidden.
在其后的action中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);
}
/** 从jsp页面hidden域中获得值 */
String token = request.getParameter(Constants.TOKEN_KEY);
if (token == null) {
return false;
}
/** 进行比较 */
return saved.equals(token);
}
resetToken方法
public synchronized void resetToken(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session == null) {
return;
}
session.removeAttribute(Globals.TRANSACTION_TOKEN_KEY);
}
其实自己完成令牌功能也相当简单