1.生成彩色验证码的原理
使用Java生成彩色验证码利用的就是Java动态生成和操作图片的技术。因为验证码的特点,需要每次生成一个随机的字符串,然后将生成的字符串以某种特殊的字体画到背景图片上。一个好的验证码,要求图片的背景也是随机的,而且要与数字有一定的混淆程度,这样即使攻击程序使用了OCR技术也很难识别出验证码来。
2.应用示例
由于生成验证码图片的操作也可能是经常被调用的,所以我们将它封装成一个JavaBean,其文件名为imageEnsure.java,具体代码实现如下所示。
package imageEnsure;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;
import javax.imageio.ImageIO;
public class imageEnsure {
public imageEnsure() {
}
private char mapTable[] = {
'a','b','c','d','e','f',
'g','h','i','j','k','l',
'm','n','o','p','q','r',
's','t','u','v','w','x',
'y','z','0','1','2','3',
'4','5','6','7','8','9'};
public String getEnsure(int width,int height,OutputStream os) {
if(width<=0) width=60;
if(height<=0) height=20;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//获取图形上下文
Graphics g = image.getGraphics();
//设置背景色
g.setColor(new Color(0xDCDCDC));
g.fillRect(0, 0, width, height);
//画边框
g.setColor(Color.black);
g.drawRect(0, 0, width-1, height-1);
//取随机产生的验证码
String strEnsure="";
//4代表4位验证码
for(int i=0;i<4;i++) {
strEnsure += mapTable[(int)(mapTable.length*Math.random())];
}
//将验证码显示到图像中
g.setColor(Color.black);
g.setFont(new Font("Atlantic Inline",Font.PLAIN,18));
String str = strEnsure.substring(0,1);
g.drawString(str, 8, 17);
str = strEnsure.substring(1, 2);
g.drawString(str, 20, 15);
str = strEnsure.substring(2, 3);
g.drawString(str, 35, 18);
str = strEnsure.substring(3, 4);
g.drawString(str, 45, 15);
//随机产生100个干扰点
Random rand = new Random();
for(int i=0;i<100;i++) {
int x=rand.nextInt(width);
int y=rand.nextInt(height);
g.drawOval(x, y, 1, 1);
}
//释放图形上下文
g.dispose();
try {
//输出图形到页面
ImageIO.write(image, "JPEG", os);
} catch(IOException e) {
return "";
}
return strEnsure;
}
}
程序首先创建一个内存图像的实例,然后获得此内存图像的图形上下文,然后利用图形上下文对象在内存图像中画背景、画边框。
随后在一个循环里随机产生4个介于0到mapTable大小之间的整数,然后以此整数为索引将其所对应的在mapTable中的字符捏起来作为验证字符串,并在内存对象中输出,最后再在内存对象中随机画了100个位置不确定的干扰点并调用ImageIO的静态方法write输入内存对象到response输出流中去。
函数的返回值为验证码对应的字符串。
读者也可以根据自己的的需要定义自己的mapTable字符集,由于输出的字符很模糊,所以读者在挑选字符集时应该尽量避免一些外形相似的字符,如大写字母O和数字0,小写字母l和数字1等。因为这里只是注重阐述原理,所以忽略了这些细节问题。
文件index.jsp用于调用这个Bean,生成验证码图片并将验证码字符添加到session的属性表中以供稍后使用。
<%@ page language="java" import="java.util.*" contentType="image/jpeg"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<jsp:useBean id="image" scope="page" class="imageEnsure.imageEnsure"/>
<%
String str=image.getEnsure(0, 0, response.getOutputStream());
//将验证码存入session
session.setAttribute("strEnsure", str);
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'index.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">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
This is my JSP page. <br>
</body>
</html>
image/jpeg指明这个JSP页面的输出内容的类型为JPEG图像,因此,在其他页面中可以将此页面当作图片文件来引用,不同的只是此图片是动态生成的,而且每次输出的都是不一样的。下面来介绍页面文件imageEnsure.jsp是怎么使用它的:
<%@ page language="java" import="java.util.*" contentType="text/html; charset=GBK"%>
<%
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>验证码输入页面</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">
<meta http-equiv="Content-Type" content="text/html;charset=GB2312">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<p>验证码:<img src="index.jsp">
<form method="post" action="action_imageEnsure.jsp">
<p>
请输入上面的验证码:<input type="text" name="code" maxlength="4">
<p>
<input type="submit" value="提交检测"></td>
</form>
</body>
</html>
可以看到,对image.jsp的引用放在了HTML语言的img标签中当成输出图片的源。该页面的作用是显示image.jsp动态生成的图片并提供一个输入框供用户输入所看到的验证码。而文件action_imageEnsure.jsp用于响应用户的输入,并执行最终的验证逻辑。
<%@ page language="java" import="java.util.*" contentType="text/html; charset=GBK"%>
<%
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>验证码输入页面</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">
<meta http-equiv="Content-Type" content="text/html;charset=GB2312">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<%
String ensure = (String)session.getAttribute("strEnsure");
String code = request.getParameter("code");
out.print("<p>"+ensure);
if(ensure.equals(code))
out.print("验证通过");
else
out.print("验证不通过");
%>
</body>
</html>
以下是执行效果:
最后好像还有点问题,贴出来大家看看。