使用Session防止表单重复提交.docx
- 文档编号:7074052
- 上传时间:2023-01-17
- 格式:DOCX
- 页数:13
- 大小:4.68MB
使用Session防止表单重复提交.docx
《使用Session防止表单重复提交.docx》由会员分享,可在线阅读,更多相关《使用Session防止表单重复提交.docx(13页珍藏版)》请在冰豆网上搜索。
使用Session防止表单重复提交
JavaWeb学习总结(十三)——使用Session防止表单重复提交
在平时开发中,如果网速比较慢的情况下,用户提交表单后,发现服务器半天都没有响应,那么用户可能会以为是自己没有提交表单,就会再点击提交按钮重复提交表单,我们在开发中必须防止表单重复提交。
一、表单重复提交的常见应用场景
有如下的form.jsp页面
1<%@pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%>
2
DOCTYPEHTML>
3
4
5
6
7
8
9
10用户名:
11
12
13
14
form表单提交到DoFormServlet进行处理
1packagexdp.gacl.session;
2
3importjava.io.IOException;
4importjavax.servlet.ServletException;
5importjavax.servlet.http.HttpServlet;
6importjavax.servlet.http.HttpServletRequest;
7importjavax.servlet.http.HttpServletResponse;
8
9publicclassDoFormServletextendsHttpServlet{
10
11publicvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)
12throwsServletException,IOException{
13//客户端是以UTF-8编码传输数据到服务器端的,所以需要设置服务器端以UTF-8的编码进行接收,否则对于中文数据就会产生乱码
14request.setCharacterEncoding("UTF-8");
15StringuserName=request.getParameter("username");
16try{
17//让当前的线程睡眠3秒钟,模拟网络延迟而导致表单重复提交的现象
18Thread.sleep(3*1000);
19}catch(InterruptedExceptione){
20e.printStackTrace();
21}
22System.out.println("向数据库中插入数据:
"+userName);
23}
24
25publicvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)
26throwsServletException,IOException{
27doGet(request,response);
28}
29
30}
如果没有进行form表单重复提交处理,那么在网络延迟的情况下下面的操作将会导致form表单重复提交多次
1.1、场景一:
在网络延迟的情况下让用户有时间点击多次submit按钮导致表单重复提交
演示动画如下所示:
1.2、场景二:
表单提交后用户点击【刷新】按钮导致表单重复提交
演示动画如下所示:
点击浏览器的刷新按钮,就是把浏览器上次做的事情再做一次,因为这样也会导致表单重复提交。
1.3、场景三:
用户提交表单后,点击浏览器的【后退】按钮回退到表单页面后进行再次提交
演示动画如下所示:
二、利用JavaScript防止表单重复提交
既然存在上述所说的表单重复提交问题,那么我们就要想办法解决,比较常用的方法是采用JavaScript来防止表单重复提交,具体做法如下:
修改form.jsp页面,添加如下的JavaScript代码来防止表单重复提交
1<%@pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%>
2
DOCTYPEHTML>
3
4
5
6
7varisCommitted=false;//表单是否已经提交标识,默认为false
8functiondosubmit(){
9if(isCommitted==false){
10isCommitted=true;//提交表单后,将表单是否已经提交标识设置为true
11returntrue;//返回true让表单正常提交
12}else{
13returnfalse;//返回false那么表单将不提交
14}
15}
16
17
18
19
20
21用户名:
22
23
24
25
我们看看使用了JavaScript来防止表单提交重复是否可以成功,运行效果如下:
可以看到,针对"在网络延迟的情况下让用户有时间点击多次submit按钮导致表单重复提交"这个应用场景,使用JavaScript是可以解决这个问题的,解决的做法就是"用JavaScript控制Form表单只能提交一次"。
除了用这种方式之外,经常见的另一种方式就是表单提交之后,将提交按钮设置为不可用,让用户没有机会点击第二次提交按钮,代码如下:
1functiondosubmit(){
2//获取表单提交按钮
3varbtnSubmit=document.getElementById("submit");
4//将表单提交按钮设置为不可用,这样就可以避免用户再次点击提交按钮
5btnSubmit.disabled="disabled";
6//返回true让表单可以正常提交
7returntrue;
8}
运行效果如下:
另外还有一种做法就是提交表单后,将提交按钮隐藏起来,这种做法和将提交按钮设置为不可用是差不多的,个人觉得将提交按钮隐藏影响到页面布局的美观,并且可能会让用户误以为是bug(怎么我一点击按钮,按钮就不见了呢?
用户可能会有这样的疑问),我个人在开发中用得比较多的是表单提交后,将提交按钮设置为不可用,反正使用JavaScript防止表单重复提交的做法都是差不多的,目的都是让表单只能提交一次,这样就可以做到表单不重复提交了。
使用JavaScript防止表单重复提交的做法只对上述提交到导致表单重复提交的三种场景中的【场景一】有效,而对于【场景二】和【场景三】是没有用,依然无法解决表单重复提交问题。
三、利用Session防止表单重复提交
对于【场景二】和【场景三】导致表单重复提交的问题,既然客户端无法解决,那么就在服务器端解决,在服务器端解决就需要用到session了。
具体的做法:
在服务器端生成一个唯一的随机标识号,专业术语称为Token(令牌),同时在当前用户的Session域中保存这个Token。
然后将Token发送到客户端的Form表单中,在Form表单中使用隐藏域来存储这个Token,表单提交的时候连同这个Token一起提交到服务器端,然后在服务器端判断客户端提交上来的Token与服务器端生成的Token是否一致,如果不一致,那就是重复提交了,此时服务器端就可以不处理重复提交的表单。
如果相同则处理表单提交,处理完后清除当前用户的Session域中存储的标识号。
在下列情况下,服务器程序将拒绝处理用户提交的表单请求:
1.存储Session域中的Token(令牌)与表单提交的Token(令牌)不同。
2.当前用户的Session中不存在Token(令牌)。
3.用户提交的表单数据中没有Token(令牌)。
看具体的范例:
1.创建FormServlet,用于生成Token(令牌)和跳转到form.jsp页面
1packagexdp.gacl.session;
2
3importjava.io.IOException;
4importjavax.servlet.ServletException;
5importjavax.servlet.http.HttpServlet;
6importjavax.servlet.http.HttpServletRequest;
7importjavax.servlet.http.HttpServletResponse;
8
9publicclassFormServletextendsHttpServlet{
10privatestaticfinallongserialVersionUID=-884689940866074733L;
11
12publicvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)
13throwsServletException,IOException{
14
15Stringtoken=TokenProccessor.getInstance().makeToken();//创建令牌
16System.out.println("在FormServlet中生成的token:
"+token);
17request.getSession().setAttribute("token",token);//在服务器使用session保存token(令牌)
18request.getRequestDispatcher("/form.jsp").forward(request,response);//跳转到form.jsp页面
19}
20
21publicvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)
22throwsServletException,IOException{
23doGet(request,response);
24}
25
26}
2.在form.jsp中使用隐藏域来存储Token(令牌)
1<%@pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%>
2
DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.01Transitional//EN">
3
4
5
6
7
8
9
10<%--使用隐藏域存储生成的token--%>
11<%--
12
13--%>
14<%--使用EL表达式取出存储在session中的token--%>
15
16用户名:
17
18
19
20
3.DoFormServlet处理表单提交
1packagexdp.gacl.session;
2
3importjava.io.IOException;
4importjavax.servlet.ServletException;
5importjavax.servlet.http.HttpServlet;
6importjavax.servlet.http.HttpServletRequest;
7importjavax.servlet.http.HttpServletResponse;
8
9publicclassDoFormServletextendsHttpServlet{
10
11publicvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)
12throwsServletException,IOException{
13
14booleanb=isRepeatSubmit(request);//判断用户是否是重复提交
15if(b==true){
16System.out.println("请不要重复提交");
17return;
18}
19request.getSession().removeAttribute("token");//移除session中的token
20System.out.println("处理用户提交请求!
!
");
21}
22
23/**
24*判断客户端提交上来的令牌和服务器端生成的令牌是否一致
25*@paramrequest
26*@return
27*true用户重复提交了表单
28*false用户没有重复提交表单
29*/
30privatebooleanisRepeatSubmit(HttpServletRequestrequest){
31Stringclient_token=request.getParameter("token");
32//1、如果用户提交的表单数据中没有token,则用户是重复提交了表单
33if(client_token==null){
34returntrue;
35}
36//取出存储在Session中的token
37Stringserver_token=(String)request.getSession().getAttribute("token");
38//2、如果当前用户的Session中不存在Token(令牌),则用户是重复提交了表单
39if(server_token==null){
40returntrue;
41}
42//3、存储在Session中的Token(令牌)与表单提交的Token(令牌)不同,则用户是重复提交了表单
43if(!
client_token.equals(server_token)){
44returntrue;
45}
46
47returnfalse;
48}
49
50publicvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)
51throwsServletException,IOException{
52doGet(request,response);
53}
54
55}
生成Token的工具类TokenProccessor
1packagexdp.gacl.session;
2
3importjava.security.MessageDigest;
4importjava.security.NoSuchAlgorithmException;
5importjava.util.Random;
6importsun.misc.BASE64Encoder;
7
8publicclassTokenProccessor{
9
10/*
11*单例设计模式(保证类的对象在内存中只有一个)
12*1、把类的构造函数私有
13*2、自己创建一个类的对象
14*3、对外提供一个公共的方法,返回类的对象
15*/
16privateTokenProccessor(){}
17
18privatestaticfinalTokenProccessorinstance=newTokenProccessor();
19
20/**
21*返回类的对象
22*@return
23*/
24publicstaticTokenProccessorgetInstance(){
25returninstance;
26}
27
28/**
29*生成Token
30*Token:
Nv6RRuGEVvmGjB+jimI/gw==
31*@return
32*/
33publicStringmakeToken(){//checkException
34//7346734837483834u93849349384938443434384
35Stringtoken=(System.currentTimeMillis()+newRandom().nextInt(999999999))+"";
36//数据指纹128位长16个字节md5
37try{
38MessageDigestmd=MessageDigest.getInstance("md5");
39bytemd5[]=md.digest(token.getBytes());
40//base64编码--任意二进制编码明文字符adfsdfsdfsf
41BASE64Encoderencoder=newBASE64Encoder();
42returnencoder.encode(md5);
43}catch(NoSuchAlgorithmExceptione){
44thrownewRuntimeException(e);
45}
46}
47}
首先访问FormServlet,在FormServlet中生成Token之后再重定向到form.jsp页面,这次是在服务器端处理表单重复提交的,运行效果如下:
从运行效果中可以看到,通过这种方式处理表单重复提交,可以解决上述的场景二和场景三中出现的表单重复提交问题。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 使用 Session 防止 表单 重复 提交