利用同步令牌来解决重读提交的基本原理
1 用户访问包含表单的页面 服务器在这次会话中 创建一个session对象 并产生一个令牌值 将这个令牌值作为隐藏输入域值 随表单一起发送到客户端 同时将令牌值保存到session中
2 用户提交页面 服务器端首先判断请求参数中的令牌值和Session中保存的令牌值是否相等 如果相等 则清楚session的令牌值 然后执行数据处理操作 如果不相等 则提示用户已经提交过表单 同时产生一个新的令牌值保存到session中 当用户重读提交数据页面的时候 将新产生的令牌值最为隐藏输入域的值
TokenProcessor类主要提供下列方法
public java.lang.String generateToken(HttpServletRequest request)
根据当前用户会话ID和当前的系统时间生成一个唯一的令牌值
public void savaToken(HttpServletRequest request)
调用generateToken()方法产生一个令牌值 并把它保存到Session中 如果Session不存在 则创建一个新的Session
public void resetToken(HttpServletRequest request)
清楚保存在用户Session中的令牌值
public boolean isTokenValid(HttpServletRequest request)
public boolean isTokenValid(HttpServletRequest request, boolean reset)
以上两种方法获取请求参数中的令牌值 并与保存在用户Session中的令牌值进行比较 判断是否相等
参数reset表示检测后是否要清楚保存在用户Session中的令牌值 前一个方法调用后一个方法 并给reset传递参数false 即在检测后不清楚Session的令牌值
例子:
1 令牌处理类 Tokenprocessor.java
2 index.jsp
增加一个隐藏域 并以服务器端产生的令牌值作为他的值
- <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
- <%@ include file="header.jsp" %> //封装了request.getContextPath
- <%@ page import="org.sunxin.ch19.util.TokenProcessor" %>
- <%
- String path = request.getContextPath();
- String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
- %>
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <base href="<%=basePath%>">
- <title>My JSP 'login.jsp' starting page</title>
- <meta http-equiv="pragma" content="no-cache">
- <meta http-equiv="cache-control" content="no-cache">
- <meta http-equiv="expires" content="0">
- <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
- <meta http-equiv="description" content="This is my page">
- </head>
- <body>
- <%
- //获取令牌类实例
- TokenProcessor processor = TokenProcessor.getInstance();
- //获取令牌值
- String token = processor.getToken(request);
- %>
- <form action="${ctx}/servlet/handle" name="theForm" method="post">
- <table>
- <tr>
- <td>用户名:</td>
- <td><input type="text" name="username"/></td>
- </tr>
- <tr>
- <td>密码:</td>
- <td>
- <input type="password" name="password"/>
- <%--设置隐藏域,其值为令牌值--%>
- <input type="hidden" name="org.sunxin.token" value="<%=token%>"/>
- </td>
- </tr>
- <tr>
- <td>
- <input type="reset" value="重设">
- </td>
- <td>
- <input type="submit" value="提交" name="btnSubmit" >
- </td>
- </tr>
- </table>
- </form>
- </body>
- </html>
3 HandlerServlet.java
- package org.sunxin.ch19.servlet;
- import java.io.IOException;
- import java.io.PrintWriter;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.sunxin.ch19.util.TokenProcessor;
- public class HandlerServlet extends HttpServlet
- {
- int count=0;
- public void doPost(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException,IOException
- {
- resp.setContentType("text/html;charset=GBK");
- PrintWriter out=resp.getWriter();
-
- TokenProcessor processor=TokenProcessor.getInstance();
- if(processor.isTokenValid(req))
- {
- try
- {
- Thread.sleep(5000);
- }
- catch(InterruptedException e)
- {
- System.out.println(e);
- }
-
- System.out.println("submit : "+count);
- if(count%2==1)
- count=0;
- else
- count++;
- out.println("success");
- }
- else
- {
- processor.saveToken(req);
- out.println("你已经提交了表单,同一表单不能提交两次。");
- }
- out.close();
- }
- }
阅读(10127) | 评论(0) | 转发(0) |