目前,不少网站为了防止用户利用机器人自动注册、登录、灌水,都采用了验证码技术。所谓验证码,就是将一串随机产生的数字或符号,生成一幅图片, 图片里加上一些干扰象素(防止OCR),由用户肉眼识别其中的验证码信息,输入表单提交网站验证,验证成功后才能使用某项功能。
验证码不同于注册码,注册码是软件作者根据提交的机器码通过特殊算法算出的,能让软件正常运行的密码。而验证码是将一串随机产生的数字或符号,生成一幅图片,图片里加上一些干扰象素(防止OCR),由用户肉眼识别其中的验证码信息,输入表单提交网站验证,验证成功后才能使用某项功能。
验证码作用:防止用户利用机器人自动注册、登录、灌水。
一.常见的验证码
1,四位数字,随机的一数字字符串,最原始的验证码,验证作用几乎为零;
2,CSDN网站用户登录用的是GIF格式,目前常用的随机数字图片验证码。图片上的字符比较中规中矩,验证作用比上一个好。没有基本图形图像学知识的人,不可破!可惜读取它的程序,在CSDN使用它的第一天,好像就在论坛里发布了;
3,QQ网站用户登录用的是PNG格式,图片用的随机数字 随机大写英文字母,每刷新一次,每个字符都会变位置呢!主要是为了防止有人利用软件批量获取QQ号码;
4,MS的hotmail申请时候的是BMP格式, 随机数字 随机大写英文字母 随机干扰像素 随机位置;
5,Google的Gmail注册时候的是JPG格式,随机英文字母 随机颜色 随机位置 随机长度;
6,其他各大论坛的是XBM格式,内容随机。
二.验证码作用分析
验证码起源——
攻击者会使用有害程序注册大量的 Web 服务帐户(如 Passport),并用这些账户进行为其他的用户制造麻烦,如发送垃圾邮件,或通过同时反复登录多个帐户来延缓服务的速度。在大多数情况下,自动注册程序不能识别此图片中的字符。
简单的说呢,验证码就是防止攻击者编写程序,自动注册,重复登录暴力破解密码。
验证码实现流程——
服务器端随机生成验证码字符串,写入session中,保存在内存中,并写入图片,发送给浏览器端显示,浏览器端输入验证码图片上字符,然后提交服务器端,提交的字符和服务器端保存的该字符比较是否一致。一致就继续,否则返回提示。
图片的字符识别,就是看图片上的干扰强度了。就实际的效果来说,验证码只是增加攻击者的难度,而不可能完全的防止。
==================================产生彩色验证码==================================
以下代码主要思想来源于:《华夏名网》
文档名:VerifyCode.java ===========================
package com.pannuo.tools; //包名
import java.awt.*;
import java.awt.image.*;
import java.util.*;
public class VerifyCode {
private String randNum="";
public Color getRandColor(int fc,int bc){//给定范围获得随机颜色
Random random = new Random();
if(fc>255) fc=255;
if(bc>255) bc=255;
int r=fc+random.nextInt(bc-fc);
int g=fc+random.nextInt(bc-fc);
int b=fc+random.nextInt(bc-fc);
return new Color(r,g,b);
}
public BufferedImage creatImage(){
// 在内存中创建图象
int width=60, height=20;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 获取图像上下文
Graphics g = image.getGraphics();
//生成随机类
Random random = new Random();
// 设定背景色
g.setColor(getRandColor(200,250));
g.fillRect(0, 0, width, height);
//设定字体
g.setFont(new Font("Times New Roman",Font.PLAIN,18));
// 随机产生155条干扰线,使图象中的认证码不易被其他程式探测到
g.setColor(getRandColor(160,200));
for (int i=0;i<155;i++) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
g.drawLine(x,y,x+xl,y+yl);
}
// 取随机产生的认证码(4位数字)
//String rand = request.getParameter("rand");
//rand = rand.substring(0,rand.indexOf("."));
for (int i=0;i<4;i++){
String rand=String.valueOf(random.nextInt(10)); //得到0~10之间的整数
randNum+=rand;
// 将认证码显示到图象中
g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110)));//调用函数出来的颜色相同,可能是因为种子太接近,所以只能直接生成
g.drawString(rand,13*i+6,16);
}
// 图象生效
g.dispose();
return image;
}
public String getRandNum() {
return randNum;
}
}
文档名:login.jsp(登录页面) ===========================
<BODY>
……
验证码<input type="text" name="verifyCode">
<img src="orderService/user/do/verifyCode" />
……
</BODY>
文档名:userServlet.java(用户的servlet) ===========================
——service(……)方法中
String pathInfo = request.getPathInfo();
if("/regist".equals(pathInfo)) {
registCheck(request,response);
} else if("/login".equals(pathInfo)) {
login(request,response);
} else if("/findPasswd".equals(pathInfo)) {
findPasswd(request,response);
} else if("/alterPasswd".equals(pathInfo)) {
alterPasswd(request,response);
} else if("/backHome".equals(pathInfo)) {
backHome(request,response);
} else if("/verifyCode".equals(pathInfo)) {
verifyCode(request,response);
} else {
super.service(request, response);
}
—— verifyCode(request,response)方法中:
private void verifyCode(HttpServletRequest request, HttpServletResponse response) {
// TODO Auto-generated method stub
VerifyCode image = new VerifyCode();
// 设置页面不缓存
response.setHeader("Pragma","No-cache");
response.setHeader("Cache-Control","no-cache");
response.setDateHeader("Expires", 0);
//这句必须写在session之前,否则在验证码验证时,从页面获得值与这里session中的不同!
BufferedImage buffImage = image.creatImage();
// 将认证码存入SESSION
HttpSession session = request.getSession();
session.setAttribute("rand",image.getSRand());
// 输出图象到页面
try {
ImageIO.write(buffImage, "JPEG", response.getOutputStream());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
——login(request,response)方法中:
private void login(HttpRequest request,HttpResponse response) throws ……{
request.setCharacterEncoding("UTF-8");
String userName = request.getParameter("user");
String password = request.getParameter("passwd");
String verifyCode = request.getParameter("verifyCode");
if(userName == null || userName == "" ||
password == null || password == "" ||
verifyCode == null || verifyCode == "") {
response.sendRedirect("/dingfan/user/login.jsp");
return;
}
String code = (String)(request.getSession().getAttribute("rand"));
if(!code.equals(verifyCode)) {
request.setAttribute("message", "登录页面:验证码错误");
RequestDispatcher rd = request.getRequestDispatcher("/user/error.jsp");
rd.forward(request, response);
return;
}
UserEntity user = UserDAO.findByUsername(userName);
if(user == null || user.getLimit().equals(Limit.admin)) {
request.setAttribute("message", "登录页面:没有此用户");
RequestDispatcher rd = request.getRequestDispatcher("/user/error.jsp");
rd.forward(request, response);
return;
}
if(user.getPassword().equals(password)) {
//验证成功则创建一个session
HttpSession session = request.getSession(true);
session.setAttribute("user", user);
backHome(request, response);
} else {
//密码不符
request.setAttribute("message", "登录页面:密码输入错误");
RequestDispatcher rd = request.getRequestDispatcher("/user/error.jsp");
rd.forward(request, response);
}
}
以下是一个临时性测试文件,一般jsp中不应该有javaBean和<%……%>。
文档名:verifyCode.jsp(对bean的引用) ===========================
<%@ page contentType="image/jpeg" import="javax.imageio.*" %>
<jsp:useBean id="image" scope="session" class="com.pannuo.tools.Image"/>
<%
//配置页面不缓存
response.setHeader("Pragma","No-cache");
response.setHeader("Cache-Control","no-cache");
response.setDateHeader("Expires", 0);
BufferedImage buffImag = image.creatImage();
// 将认证码存入SESSION
session.setAttribute("rand",image.sRand);
// 输出图象到页面
ImageIO.write(buffImg, "JPEG", response.getOutputStream());
%>