验证码有多种,写法也有多种,有前端写的也有后台写的,不推荐前端写的,因为如果浏览器禁用js,就可以跳过验证码验证。
下面我写的是使用jsp+response制作验证码。(算是原创吧?学来的算是我的吧)
验证码制作步骤:
response:HttpServletResponse 代表服务器对用户响应
-
设置页面的响应方式
response.setContentType("image/jpeg");//将页面的响应方式设置为图片格式
注:响应方式是告诉浏览器用什么格式来响应收到的数据
-
去掉页面的缓存
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
- 在Java中创建Image对象,获取画笔,绘画矩形、字符、干扰线。
- 获取通向用户浏览器的字节流,向用户的浏览器发送图片,比如验证码。
正式开始。
服务端产生验证码。 code.jsp
<%@page import="javax.imageio.ImageIO"%>
<%@page import="java.util.Random"%>
<%@page import="java.awt.image.BufferedImage"%>
<%@page import="java.awt.*"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
//设置页面不缓存。因为某些浏览器缓存验证码,导致重新打开该页面时总提示验证码过期。
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/jpeg");//将页面的响应方式设置为图片格式
// 在内存中创建图象
int width = 60, height = 20; //验证码的宽和高
/*定义画布。
在服务器内存中创建一个Image对象,指定图片的宽度、高度及类型。
Image是抽象类,不能new,要使用它的父类BufferedImage 参数(宽度,高度,图片的类型)*/
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//获取图片上面的画笔
Graphics g = image.getGraphics();
//使用画笔在图片上面画一个矩形
g.setColor(new Color(244,226,192));//设置背景的颜色
g.fillRect(0, 0, width, height);//实心的。坐标为0,0
String code="";//随机产生4位验证码
for(int i=0;i<4;i++){//使4个字符的颜色大小不同
String s=randomC();
g.setColor(new Color((int)(Math.random()*255),(int)(Math.random()*255),(int)(Math.random()*255)));//设置画笔的颜色为随机色
g.setFont(new Font("黑体",0,r.nextInt(10)+10));
g.drawString(s, i*15, 16);//将字符画上去(设置字符的宽度使得4个字符不会叠在一起)
code=code+s;
}
session.setAttribute("code", code);
// 随机产生干扰线,使图象中的认证码不易被其它程序探测到
for(int i=0;i<10;i++){
g.setColor(new Color((int)(Math.random()*255),(int)(Math.random()*255),(int)(Math.random()*255)));//设置画笔的颜色为随机色
g.drawLine(r.nextInt(100), r.nextInt(40), r.nextInt(100), r.nextInt(40));
}
//将Image对象使用字节流发送给用户的电脑
ImageIO.write(image, "JPEG", response.getOutputStream());
//加上下面代码,运行时才不会出现java.lang.IllegalStateException: getOutputStream() has already been called ..........异常
response.getOutputStream().flush(); //将流刷新
response.getOutputStream().close(); //关闭流
response.flushBuffer();
out.clear();
out = pageContext.pushBody();
/* 由于jsp container在处理完成请求后会调用releasePageContet方法释放所用的PageContext object,
并且同时调用getWriter方法,由于getWriter方法与在jsp页面中使用流相关的getOutputStream方法冲突,所以会造成这种异常 */
%>
<%!
//返回随机字符
Random r=new Random();
public String randomC(){
String str="";
int n=r.nextInt(3);//产生0、1、2
if(n==0){
str=""+(char)(r.nextInt(26)+97);//小写字母
}else if(n==1){
str=""+(char)(r.nextInt(26)+65);//大写字母
}else{
str=""+(char)(r.nextInt(10)+48);//数字
}
return str;
}
%>
前台接收验证码显示,并且用户点击图片可更换验证码。页面很丑,随便写的只是为了测试程序。 login.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>
<style type="text/css">
img{
border:solid 1px red;
width:80px;
height:30px;
}
</style>
<!-- <script type="text/javascript">
function f_change(img){
img.src="code.jsp?id="+new Date().getTime();//src=code.jsp点击的地址跟之前是一样的,浏览器懒得存下来。拼接一个参数,使得每次点击的不同。new Date如果用户点击时间间隔过短,不会改变。getTime()毫秒不会一样。
}
</script> -->
</head>
<body>
<form action="check.jsp">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
验证码:<input type="text" name="code" size="4"><img alt="验证码" src="code.jsp" onclick="this.src='code.jsp?id='+Math.random()"><br>
<input type="submit">
</form>
</body>
</html>
如果只有前面两个jsp代码,不会出结果:验证码输出正确还是错误。 check.jsp
在服务端产生验证码时将验证码添加到session中,获取用户提交的验证码,进行比较。比较后的结果随意,我这里只是为了测试,并且我写的是忽略大小写。
<%@ 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>
<%
String userCode=request.getParameter("code");
String imageCode=(String)session.getAttribute("code");
if(userCode.equalsIgnoreCase(imageCode)){
%>
<script type="text/javascript">
alert("验证码正确");
</script>
<%
}else{%>
<script type="text/javascript">
alert("验证码错误");
history.back();
</script>
<%
}
%>
</body>
</html>
过程中会报异常:java.lang.IllegalStateException: getOutputStream() has already been called for this response
不要慌,这是正常的。解决的代码上面已经写了哦,但是那几行是套用别人的。
貌似是容器有自动调用response.getWriter(),和response.getOutputStream()相冲突的,所以会出现以上这个异常。但是我还不是很明白为什么会有getWriter()、怎么看产生了getWriter()以及为什么要这么解决。
还可以下载validatecode.jar包,利用验证码工具ValidateCode来实现,只需要指定验证码的宽、高、字符数量、干扰线数量,它就会自己画好随机验证码,效果是一样的。validatecode.jar我找不到在哪里下载,找到的一个竟然还要钱,所以这里就不写了。
如果上方有什么不对的地方敬请指教!我的疑问也希望能有人解答,谢谢。