一.常见的验证码
1,四位数字,随机的一数字字符串,最原始的验证码,验证作用几乎为零。
2,CSDN网站用户登录用的是GIF格式,目前常用的随机数字图片验证码。图片上的字符比较中规中矩,验证作用比上一个好。没有基本图形图像学知识的人,不可破!可惜读取它的程序,在CSDN使用它的第一天,好像就在论坛里发布了,真是可怜!
3,QQ网站用户登录用的是PNG格式,图片用的随机数字+随机大写英文字母,整个构图有点张扬,每刷新一次,每个字符还会变位置呢!有时候出来的图片,人眼都识别不了,厉害啊…
4,MS的hotmail申请时候的是BMP格式, 随机数字+随机大写英文字母+随机干扰像素+随机位置。
5,Google的Gmail注册时候的是JPG格式,随机英文字母+随机颜色+随机位置+随机长度。
6,其他各大论坛的是XBM格式,内容随机。
二.验证码作用分析
验 证码起源:因为攻击者会使用有害程序注册大量的 Web 服务帐户(如 Passport)。攻击者可以使用这些帐户为其他的用户制造麻烦,如发送垃圾邮件或通过同时反复登录多个帐户来延缓服务的速度。在大多数情况下,自动注 册程序不能识别此图片中的字符。简单的说呢,就是防止攻击者编写程序,自动注册,重复登录暴力破解密码。验证码技术应运而生。
验证码实现 流程:服务器端随机生成验证码字符串,保存在内存中,并写入图片,发送给浏览器端显示,浏览器端输入验证码图片上字符,然后提交服务器端,提交的字符和服 务器端保存的该字符比较是否一致。一致就继续,否则返回提示。攻击者编写的robot程序,很难识别验证码字符,顺利的完成自动注册,登 录。。。。。。。。。而用户可以识别填写,所以这就实现了阻挡攻击的作用。而图片的字符识别,就是看图片上的干扰强度了。就实际的效果来说,验证码只是增 加攻击者的难度,而不可能完全的防止。
*************************************************************************************************************
在web应用中部署验证码(jsp,jsf两种方式)
在web系统中,验证码的应用基本上随处可见.验证码可以防止他人恶意攻击和垃圾注册,可以说已成了web开发中必不可少的环节.遗憾的是,验证码 在jsp,jsf的组件库, 至少是一些标准的组件库中并没有出现.本文分别介绍如何在jsp和jsf中使用验证码和我的一些小经验,呵呵.
在jsp中,我们使用apache的taglibs-image:
http://jakarta.apache.org/taglibs/sandbox/doc/image-doc/intro.html
可以简便的配置自己的验证码.而由于在jsf中,无法和其他jsp标签库混用(至少不能和上述标签库混用),我们则用Java2D自己绘制验证码图.
1. 在jsp中使用taglibs-image部署验证码
taglibs-image可以通过标签自动将一段文字和背景图片生成新的图片,文字的布局,颜色,字体等等都可以自我定制,因此拿来做验证码是非常的简单。
<%@ taglib uri="http://jakarta.apache.org/taglibs/image-1.0" prefix="img" %>
<html>
<head>
<title>ImageTagexamples</title>
<metahttp-equiv="Content-Type"content="text/html;charset=iso-8859-1">
</head>
<body>
<%
int num=(int)java.lang.Math.round(java.lang.Math.random()*8999);
String sRand=""+(1000+num);
session.setAttribute("userInfo.authcode",sRand);
%>
<img:image src="/images/auth.jpg" refresh="true">
<img:text text="<%=sRand.substring(0,1)%>" x="18%" y="25%" font="Arial" bold="true" size="16" />
<img:text text="<%=sRand.substring(1,2)%>" x="36%" y="15%" font="TimesNewRoman" bold="true" size="20"/>
<img:text text="<%=sRand.substring(2,3)%>" x="60%" y="20%" font="Arial" bold="true" size="18" />
<img:text text="<%=sRand.substring(3,4)%>" x="77%" y="30%" font="TimesNewRoman" bold="true" size="14" />
</img:image>
</body>
</html>
其中最开始百分号内的java代码是为了生成验证码,然后保存在session中.同时验证码和背景图片生成新的验证图.用户根据此图输入验证码.在服务器方,只用把用户提交表单中的验证码内容取出和session中保存的验证码对比,就可以判断正确性咯
![](https://i-blog.csdnimg.cn/blog_migrate/ed57ed2f6e3d73f9e49c69e4f30b593a.png)
2.JSF
jsf中无法使用上述标签(会无法渲染出来), 因此,我们自己实现一个生成验证图的类,再通过jsf的<h:graphicImage>标签得以显示.
生成验证码的java类如下:
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import javax.imageio.ImageIO;
/**
* 用来自动生成验证图和验证码,验证图是背景图加上干扰点加上验证码
*
* @author td
*
*/
public final class CodeImageGenerator {
private final static int DEF_WIDTH = 60;
private final static int DEF_HEIGHT = 20;
private final static String BASE_PATH = "validate-images";
/**
* 验证码
*/
private String code = "";
/**
* 验证图的地址
*/
private String path;
private int width;
private int height;
private BufferedImage image;
/**
* 验证图对应的File对象
*/
private File target;
public CodeImageGenerator() {
this(DEF_WIDTH, DEF_HEIGHT);
}
public CodeImageGenerator(int width, int height) {
this.width = width;
this.height = height;
generateCodeImage();
}
/**
* 生成验证码和验证图
*
*/
private void generateCodeImage() {
// create the image
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
// set the background color
g.setColor(new Color(0xDCDCDC));
g.fillRect(0, 0, width, height);
// draw the border
g.setColor(Color.black);
g.drawRect(0, 0, width - 1, height - 1);
// set the font
g.setFont(new Font("Times New Roman", Font.PLAIN, 18));
// create a random instance to generate the codes
Random random = new Random();
// make some confusion
for (int i = 0; i < 50; i++) {
int x = random.nextInt(width);
int y = random.nextInt(height);
g.drawOval(x, y, 0, 0);
} // generate a random code
for (int i = 0; i < 4; i++) {
String rand = String.valueOf(random.nextInt(10));
code += rand;
g.drawString(rand, 13 * i + 6, 16);
}
g.dispose();
try {
File dir = new File("K:/Tomcat 5.5/webapps/nirvana/validate-images");
String s = new Double(Math.random() * 995596390219L).toString();
File imgFile = new File(dir, s + ".jpeg");
if (!imgFile.exists())
imgFile.createNewFile();
target = imgFile;
ImageIO.write(image, "JPEG", imgFile);
path = "/" + BASE_PATH + "/" + s + ".jpeg";
System.err.println(path);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public BufferedImage getImage() {
return image;
}
public String getCode() {
if (code == null)
code = "";
return code;
}
public static void main(String[] args) throws Exception {
// File imgFile = new File("codeImage.jpeg");
// CodeImageGenerator cig = new CodeImageGenerator();
// ImageIO.write(cig.getImage(), "JPEG", imgFile);
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
/**
* 当这个对象被回收时,同时销毁其对应的验证图
*/
@Override
protected void finalize() throws Throwable {
// TODO Auto-generated method stub
// System.err.println("finalize");
if (target.exists())
target.delete();
super.finalize();
}
public File getTarget() {
return target;
}
public void setTarget(File target) {
this.target = target;
}
}
要说明几点的事,这个类会把生成的验证图放在制定文件夹下,未免得文件越来越多,应该当验证图不再使用时将之删除.所以此类重写了Object的finalize()方法,当此类被垃圾回收器回收时,同时也删除其对应的验证图.
这样,就可以利用java的垃圾回收器轻松为我们删除不用的文件.
另外,在页面对应的managed-bean中,我们还要添加如何得到验证码和验证图的方法
private String validate_code;
public CodeImageGenerator getValidator() {
if(validator!=null){
validator.getTarget().delete();
validator=null;
}
validator=new CodeImageGenerator();
System.out.println(validator.getCode());
return validator;
}
public void setValidator(CodeImageGenerator validator) {
this.validator = validator;
}
其中validate-code对应用户输入的验证码信息
因为每次刷新页面都需要得到不同的验证码,所以在getValidator()方法时,每次需要返回一个新的CodeImageGenerator.同时,你可能等不及垃圾回收器帮你删除文件,因此,可以在这里同时删除老的验证图.
另外,在注册时我们还需要做一下判断:
// System.out.println("haha");
if(!validator.getCode().equals(validate_code)){
FacesMessage message = new FacesMessage(
FacesMessage.SEVERITY_ERROR, "验证码错误",
"验证码错误");
FacesContext.getCurrentInstance().addMessage(null, message);
FacesContext fcg = FacesContext.getCurrentInstance();
((LoginBean) fcg.getApplication().getVariableResolver()
.resolveVariable(fcg, "loginBean")).setReg(true);
System.out.println(validator.getCode());
System.out.println(validate_code);
return null;
}
..................
}
最后,我们需要在页面中添加对应的标签
< h:message for = " vcode " styleClass = " error " ></ h:message >
< h:inputText id = " vcode " required = " true " value = " #{myPageBean.validate_code} " ></ h:inputText >
< h:graphicImage value = " #{myPageBean.validator.path} " ></ h:graphicImage >
这样, 我们就在jsf中实现了自己的验证码部署^_^
![](https://i-blog.csdnimg.cn/blog_migrate/0d0c6bec36d0eb9507b12e7bdab84970.png)
****************************************************************************************************************
图形验证码识别技术
项目需要,要在首页登录界面添加一个图形验证码,赶时髦吧,网上一搜,特别多,找了几个,都不太满意。主要问题是大部分代码生成的图片宽度不唯一, 页面布局不容易控制,其次是颜色单一,有些又过于抽象,不仔细看很容易弄错。针对特定的客户,我只需要“图片”长宽固定,颜色多样的数字图形验证码,借鉴 网上的现有代码,自己操刀完成,以下是效果图:
原理不复杂,就是把网页当画布,运用各色画笔,在特定区域内画出数字,然后以特定格式(本例为PNG格式)发回客户端,在IE中显示为图片,用于验证的字符串存于Session中。
主要代码如下:
// 生成随机数字字符串
public string GetRandomNumberString(int int_NumberLength)
{
string str_Number = string.Empty;
Random theRandomNumber = new Random();
for (int int_index = 0; int_index < int_NumberLength; int_index++)
str_Number += theRandomNumber.Next(10).ToString();
return str_Number;
}
生成随机颜色
public Color GetRandomColor()
{
Random RandomNum_First = new Random((int)DateTime.Now.Ticks);
// 对于C#的随机数,没什么好说的
System.Threading.Thread.Sleep(RandomNum_First.Next(50));
Random RandomNum_Sencond = new Random((int)DateTime.Now.Ticks);
// 为了在白色背景上显示,尽量生成深色
int int_Red = RandomNum_First.Next(256);
int int_Green = RandomNum_Sencond.Next(256);
int int_Blue = (int_Red + int_Green > 400) ? 0 : 400 - int_Red - int_Green;
int_Blue = (int_Blue > 255) ? 255 : int_Blue;
return Color.FromArgb(int_Red, int_Green, int_Blue);
}
根据验证字符串生成最终图象
public void CreateImage(string str_ValidateCode)
{
int int_ImageWidth = str_ValidateCode.Length * 13;
Random newRandom = new Random();
// 图高20px
Bitmap theBitmap = new Bitmap(int_ImageWidth, 20);
Graphics theGraphics = Graphics.FromImage(theBitmap);
// 白色背景
theGraphics.Clear(Color.White);
// 灰色边框
theGraphics.DrawRectangle(new Pen(Color.LightGray, 1), 0, 0, int_ImageWidth - 1, 19);
// 10pt的字体
Font theFont = new Font(Arial, 10);
for (int int_index = 0; int_index < str_ValidateCode.Length; int_index++)
{
string str_char = str_ValidateCode.Substring(int_index, 1);
Brush newBrush = new SolidBrush(GetRandomColor());
Point thePos = new Point(int_index * 13 + 1 + newRandom.Next(3), 1 + newRandom.Next(3));
theGraphics.DrawString(str_char, theFont, newBrush, thePos);
}
// 将生成的图片发回客户端
MemoryStream ms = new MemoryStream();
theBitmap.Save(ms, ImageFormat.Png);
Response.ClearContent(); //需要输出图象信息 要修改HTTP头
Response.ContentType = image/Png;
Response.BinaryWrite(ms.ToArray());
theGraphics.Dispose();
theBitmap.Dispose();
Response.End();
}
最后在Page_Load中调用以上代码
private void Page_Load(object sender, System.EventArgs e)
{
if(!IsPostBack)
{
// 4位数字的验证码
string str_ValidateCode = GetRandomNumberString(4);
// 用于验证的Session
Session[ValidateCode] = str_ValidateCode;
CreateImage(str_ValidateCode);
}
}
使用的时候在页面中加入一个Image,将图片路径改为ValidateCode.aspx的相对路径即可
<img src=ValidateCode.aspx />在需要验证的地方填入如下代码:
if (TextBox1.Text == Session[ValidateCode].ToString())
{
TextBox1.Text = 正确!;
}
else
TextBox1.Text = 错误!;OK,基本搞定,总结一下:
优点:1. 简单明了,适于简单运用
2. 界面友好,图片长宽格式固定
缺点:1. 如果有多个页面都需要此验证码,则会导致Session被其它页面重写的情况,可以考虑指定具体Session值为效验值
2. 暂时只支持数字,不过更改GetRandomNumberString()中的代码可以实现指定字符机的随机字符串
3. 页面刷新后验证码随之改变
jsp教是:JSP彩色验证码。
生成有4个随机数字和杂乱背景的图片,数字和背景颜色会改变,服务器端刷新(用history.go(-1)也会变)
原型参考ALIBABA http://china.alibaba.com/member/showimage
产生验证码图片的文件-----image.jsp
<%@ page contentType="image/jpeg" import="java.awt.*,java.awt.image.*,java.util.*,javax.imageio.*" %>
<%!
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);
}
%>
<%
//设置页面不缓存
response.setHeader("Pragma","No-cache");
response.setHeader("Cache-Control","no-cache");
response.setDateHeader("Expires", 0);
// 在内存中创建图象
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));
//画边框
//g.setColor(new Color());
//g.drawRect(0,0,width-1,height-1);
// 随机产生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 sRand="";
for (int i=0;i<4;i++){
String rand=String.valueOf(random.nextInt(10));
sRand+=rand;
// 将认证码显示到图象中
g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110))); //调用函数出来的颜色相同,可能是因为种子太接近,所以只能直接生成
g.drawString(rand,13*i+6,16);
}
// 将认证码存入SESSION
session.setAttribute("rand",sRand);
// 图象生效
g.dispose();
// 输出图象到页面
ImageIO.write(image, "JPEG", response.getOutputStream());
%>
---------------使用验证码图片的文件---------a.jsp------------------------------------
<%@ page contentType="text/html;charset=gb2312" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>认证码输入页面</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Cache-Control" CONTENT="no-cache">
<META HTTP-EQUIV="Expires" CONTENT="0">
</head>
<body>
<form method=post action="check.jsp">
<table>
<tr>
<td align=left>系统产生的认证码:</td>
<td><img border=0 src="image.jsp"></td>
</tr>
<tr>
<td align=left>输入上面的认证码:</td>
<td><input type=text name=rand maxlength=4 value=""></td>
</tr>
<tr>
<td colspan=2 align=center><input type=submit value="提交检测"></td>
</tr>
</form>
</body>
</html>
-----------------验证的页面----------check.jsp
<%@ page contentType="text/html; charset=gb2312" language="java" import="java.
<html><head>
<title>认证码验证页面</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Cache-Control" CONTENT="no-cache">
<META HTTP-EQUIV="Expires" CONTENT="0">
</head>
<body>
<%
String rand = (String)session.getAttribute("rand");
String input = request.getParameter("rand");
%>
系统产生的认证码为: <%= rand %><br>
您输入的认证码为: <%= input %><br>
<br>
<%
if (rand.equals(input)) {
%>
<font color=green>输入相同,认证成功!</font>
<%
} else {
%>
<font color=red>输入不同,认证失败!</font>
<%
}
%>
</body>
</html>