底层原理
表单的重复提交
1)重读提交的情况:(就是说发了2次表单提交的请求,而且发送的请求参数一致)
①.在表单提交到一个Servlet,而Servlet又通过请求转发的方式响应了一个jsp或者html页面这个时候地址栏里面还保留着
Servlet的路径,而我在响应页面点击刷新。(http://localhost:8080/sessionx/sub.s?name=sdfe)一直刷新这个相当于一直给Servlet发送请求
②.在响应页面没有到达时重复点击提交按钮。(一直提交给Servlet)
③.点击返回,再点击提交。(以前写的参数还在)
2)如何避免表单的重复提交:
在表单中做一个标记,提交到Servlet时,检查标记是否存在且是否与预定义的标记一致,若一致,则受理请求,并销毁标记
若不一致或者没有标记,则直接响应提交信息:
①.把标记放在request中,行不通,因为表单页面被刷新后,request被销毁了,再提交表单是一个新的request.(很重要 看submit1.jsp和success)
②把标记放在session中(很重要 看submit2.jsp和success) form的get和post那个传递的参数名和值不管提交多少次都在的 比如op = ... 再servlet的提交url中不管刷新多少次再Servlet中都能获取
submit1.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
request.setAttribute("arr", "abc");
System.out.println(request.getAttribute("arr"));
%>
<!DOCTYPE html >
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="<%=request.getContextPath() %>/subx.s" method="post">
<input type="text" name="name">
<input type="submit" value="Sub">
</form>
</body>
</html>
SubxServlet(映射为subx.s)
package cn.itcast.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/subx.s")
public class SubxServlet extends BasicServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
String arr = (String)req.getAttribute("arr");
System.out.println(arr);
if(arr!=null){
req.removeAttribute("arr");
}else{
resp.sendRedirect(req.getContextPath()+"/success/tt.jsp");
return ;
}
String name = req.getParameter("name");
System.out.println("hello "+name);
req.getRequestDispatcher("/success/success.jsp");
}
}
submit2.jsp
<%@page import="java.util.Date"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
String date = new Date().getTime()+"";
session.setAttribute("arr", date);
System.out.println(session.getAttribute("arr"));
%>
<!DOCTYPE html >
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="<%=request.getContextPath() %>/subxy.s" method="post">
<input type="hidden" name="op" value="<%=date%>">
<input type="text" name="name">
<input type="submit" value="Sub">
</form>
</body>
</html>
SubxyServlet(映射为subxy.s)
package cn.itcast.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/subxy.s")
public class SubxyServlet extends BasicServlet {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
// TODO Auto-generated method stub
String arr = req.getParameter("op");
String arr2 = (String) req.getSession().getAttribute("arr");
System.out.println("aqswd"+ arr2);
if(arr!=null && arr.equals(arr2)){
req.getSession().removeAttribute("arr");
}else{
resp.sendRedirect(req.getContextPath()+"/success/tt.jsp");
return ;
}
String name = req.getParameter("name");
System.out.println("hello2 "+name);
req.getRequestDispatcher("/success/success.jsp");
}
}
下面2个jsp在session文件夹下
success.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
Object name = request.getParameter("name");
%>
<!DOCTYPE html >
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h4>success <%=(name == null)?"":name %></h4>
</body>
</html>
tt.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html >
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h4>不好意思,您已经重读提交了</h4>
</body>
</html>
其实验证码的底层就是跟表单的重复提交一样,先将验证码的信息放入到session然后在Servlet中比较获取到的验证码值和session中的验证码值是否想通过-->若相同则表示验证码验证通过不相同则表示验证码验证不通过。
实现
文件目录结构
image.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html >
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- 这里的new Date()是js中的 -->
<form action="<%=request.getContextPath() %>/CheckServlet" method="post">
name: <input type="text" name="name"/><br/>
checkCode:<input type="text" name="code"><br/>
<!-- img的src属性值是不能改变的 一定要映射到ValidateColorServlet文件-->
<!-- 添加onclick是通过为src根据时间设置不同值从而实现每点击一次验证码换一个验证码 -->
<img id="img" alt="" src="<%=request.getContextPath() %>/ValidateColorServlet"
onclick='src="<%=request.getContextPath() %>/ValidateColorServlet?"+new Date().getTime()'><br/>
<input type="submit" value="SUB">
</form>
</body>
</html>
ValidateColorServlet.java
package cn.itcast.util;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ValidateColorServlet extends HttpServlet {
public static final String CHECK_CODE_KEY = "CHECK_CODE_KEY";
private static final long serialVersionUID = 1L;
//设置验证图片的宽度, 高度, 验证码的个数
private int width = 100;
private int height = 20;
private int codeCount = 3;
//验证码字体的高度
private int fontHeight = 1;
//验证码中的单个字符基线. 即:验证码中的单个字符位于验证码图形左上角的 (codeX, codeY) 位置处
private int codeX = 0;
private int codeY = 0;
//验证码由哪些字符组成
char [] codeSequence = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz23456789".toCharArray();
//初始化验证码图形属性
public void init(){
fontHeight = height - 2;
codeX = width / (codeCount + 2);
codeY = height - 4;
}
public void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//定义一个类型为 BufferedImage.TYPE_INT_BGR 类型的图像缓存
BufferedImage buffImg = null;
buffImg = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
//在 buffImg 中创建一个 Graphics2D 图像
Graphics2D graphics = null;
graphics = buffImg.createGraphics();
//设置一个颜色, 使 Graphics2D 对象的后续图形使用这个颜色
graphics.setColor(Color.WHITE);
//填充一个指定的矩形: x - 要填充矩形的 x 坐标; y - 要填充矩形的 y 坐标; width - 要填充矩形的宽度; height - 要填充矩形的高度
graphics.fillRect(0, 0, width, height);
//创建一个 Font 对象: name - 字体名称; style - Font 的样式常量; size - Font 的点大小
Font font = null;
font = new Font("", Font.BOLD, fontHeight);
//使 Graphics2D 对象的后续图形使用此字体
graphics.setFont(font);
graphics.setColor(Color.BLACK);
//绘制指定矩形的边框, 绘制出的矩形将比构件宽一个也高一个像素
graphics.drawRect(0, 0, width - 1, height - 1);
//随机产生 15 条干扰线, 使图像中的认证码不易被其它程序探测到
Random random = null;
random = new Random();
graphics.setColor(Color.GREEN);
for(int i = 0; i < 50; i++){
int x = random.nextInt(width);
int y = random.nextInt(height);
int x1 = random.nextInt(20);
int y1 = random.nextInt(20);
graphics.drawLine(x, y, x + x1, y + y1);
}
//创建 randomCode 对象, 用于保存随机产生的验证码, 以便用户登录后进行验证
StringBuffer randomCode;
randomCode = new StringBuffer();
for(int i = 0; i < codeCount; i++){
//得到随机产生的验证码数字
String strRand = null;
strRand = String.valueOf(codeSequence[random.nextInt(36)]);
//把正在产生的随机字符放入到 StringBuffer 中
randomCode.append(strRand);
//用随机产生的颜色将验证码绘制到图像中
graphics.setColor(Color.RED);
graphics.drawString(strRand, (i + 1)* codeX, codeY);
}
//再把存放有所有随机字符的 StringBuffer 对应的字符串放入到 HttpSession 中
request.getSession().setAttribute(CHECK_CODE_KEY, randomCode.toString());
//禁止图像缓存
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
//将图像输出到输出流中
ServletOutputStream sos = null;
sos = response.getOutputStream();
ImageIO.write(buffImg, "jpeg", sos);
sos.close();
}
}
CheckServlet.java
package cn.itcast.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import cn.itcast.util.ValidateColorServlet;
@WebServlet("/CheckServlet")
public class CheckServlet extends BasicServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
String name = req.getParameter("name");
String code = req.getParameter("code"); //获取输入的值
//在ValidateColorServlet中将图片中的的验证码的值添加到session中
/*
再把存放有所有随机字符的 StringBuffer 对应的字符串放入到 HttpSession 中
request.getSession().setAttribute(CHECK_CODE_KEY, randomCode.toString());
*/
String vcode = (String) req.getSession().getAttribute(ValidateColorServlet.CHECK_CODE_KEY);
if(code!=null&&code.equalsIgnoreCase(vcode)){
req.getRequestDispatcher("/success/success.jsp").forward(req, resp);
}else{
resp.sendRedirect(req.getContextPath()+"/success/tt.jsp");
}
}
}