1.图文验证码的原理
在servlet中随机生成一个指定位置的验证码,一般为四位,然后把该验证码保存到session中.在通过Java的绘图类以图片的形式输出该验证码。为了增加验证码的安全级别,可以输出图片的同时输出干扰线,最后在用户提交数据的时候,在服务器端将用户提交的验证码和Session保存的验证码进行比较。
2.验证码所需的技术
i.因为验证码中的文字,数字,应为都是可变的,故要用到随机生成数技术。
ii.如果验证码中包含汉字,则要用到汉字生成技术.
iii.可以使用Ajax技术实现局部刷新
iv.可以使用图片的缩放和旋转技术,
vi.随机绘制干扰线(可以是折现,直线等)
vii.如果考虑到验证码的安全性,可以使用MD5加密.
验证码模块实例
1.编写生成英文,数字,汉字随机生成的Servlet类.源代码如下:
package com.servlet;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* 随机生成英文,数字,汉字验证码
*
*
*/
public class PictureCheckCode extends HttpServlet{
//定义变量
//序列化 Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。
private static final long serialVersionUID = 1L;
//构造方法
public PictureCheckCode() {
super();
}
//重写destory方法(服务结束时,调用destory()方法,在init()方法中创建的任何资源都应该被清除和释放)
public void destroy() {
super.destroy();
}
//重写init方法(在init()方法中,servlet创建和初始化它在处理请求时需要用到的资源)
public void init() throws ServletException {
super.init();
}
//获得随机生成的颜色
public Color getRandColor(int s, int e){
Random random = new Random();
if(s > 255) s = 255;
if(e > 255) e = 255;
int r, g, b;
r = s + random.nextInt(e - s); //随机生成RGB颜色中的r值
g = s + random.nextInt(e - s); //随机生成RGB颜色中的g值
b = s + random.nextInt(e - s); //随机生成RGB颜色中的b值
return new Color(r, g, b);
}
//重写service方法
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//设置不缓存
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "No-cache");
//设置过期的时间期限
response.setDateHeader("Expires", 0);
//指定生成的响应图片,一定不能缺少这句话,否则错误.
response.setContentType("image/jpeg");
//指定生成验证码的宽度和高度
int width = 86;
int height = 22;
//创建BufferedImage对象,其作用相当于一图片
BufferedImage image =new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//创建Graphics对象,其作用相当于画笔
Graphics g = image.getGraphics();
//创建Grapchics2D对象(绘制2D图形)
Graphics2D g2d = (Graphics2D)g; //把Graphics类的对象g,强制转换成Graphics2D类的对象g2d
Random random = new Random();
//定义字体样式(bold 粗体)
Font mfont = new Font("楷体", Font.BOLD, 16);
g.setColor(getRandColor(200, 250));
//绘制背景
g.fillRect(0, 0, width, height);
//设置字体
g.setFont(mfont);
g.setColor(getRandColor(180, 200));
//绘制100条颜色和位置全部为随机产生的线条,该线条为2f
for(int i = 0; i < 100; i++){
int x = random.nextInt(width - 1);
int y = random.nextInt(height - 1);
int x1 = random.nextInt(6) + 1;
int y1 = random.nextInt(12) + 1;
//定制线条样式(CAP_BUTT为一种线端 JOIN_BEVEL两条线连接时,连接处的形状)
BasicStroke bs = new BasicStroke(2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL);
//Line2D。Double 根据指定坐标(x + x1, y + y1) (x, y)构造一条线
Line2D line = new Line2D.Double(x, y, x + x1, y + y1);
g2d.setStroke(bs);
//绘制直线
g2d.draw(line);
}
//输出由英文,数字,和中文随机组成的验证文字,具体的组合方式根据生成随机数确定。
String sRand = "";
String ctmp = "";
int itmp = 0;
//制定输出的验证码为四位
for(int i = 0; i < 4; i++){
switch(random.nextInt(3)){
case 1:
itmp = random.nextInt(26) + 65; //生成A-Z的字母
ctmp = String.valueOf((char)itmp);
break;
case 2: //生成汉字
String[] rBase= {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
//生成第一位区码
int r1 = random.nextInt(3) + 11; //生成11到14之间的随机数
String str_r1 = rBase[r1];
//生成第二位区码
int r2;
if(r1 == 13){
r2 = random.nextInt(7); //生成0到7之间的随机数
}else{
r2 = random.nextInt(16); //生成0到16之间的随机数
}
String str_r2 = rBase[r2];
//生成第一位位码
int r3 = random.nextInt(6) + 10; //生成10到16之间的随机数
String str_r3 = rBase[r3];
//生成第二位位码
int r4;
if(r3 == 10){
r4 = random.nextInt(15) + 1; //生成1到16之间的随机数
}else if(r3 == 15){
r4 = random.nextInt(15); //生成0到15之间的随机数
}else{
r4 = random.nextInt(16); //生成0到16之间的随机数
}
String str_r4 = rBase[r4];
//将生成的机内码转换为汉字
byte[] bytes = new byte[2];
//将生成的区码保存到字节数组的第一个元素中
String str_12 = str_r1 + str_r2;
int tempLow = Integer.parseInt(str_12, 16);
bytes[0] = (byte) tempLow;
//将生成的位码保存到字节数组的第二个元素中
String str_34 = str_r3 + str_r4;
int tempHigh = Integer.parseInt(str_34, 16);
bytes[1] = (byte)tempHigh;
ctmp = new String(bytes); //根据字节数组生成汉字
//测试参数
// System.out.println("生成汉字:" + ctmp);
break;
default:
itmp = random.nextInt(10) + 48; //生成0~9的数字
ctmp = String.valueOf((char)itmp);
break;
}
sRand += ctmp;
Color color = new Color(20 + random.nextInt(110), 20 + random.nextInt(110), random.nextInt(110));
g.setColor(color);
//将生成的随机数进行随机缩放并旋转制定角度 PS.建议不要对文字进行缩放与旋转,因为这样图片可能不正常显示
//将文字旋转制定角度
Graphics2D g2d_word = (Graphics2D)g;
AffineTransform trans = new AffineTransform();
//初始化 RotateTransform 类的一个新实例,该实例具有指定的角度和中心点
trans.rotate((45) * 3.14 / 180, 15 * i + 8, 7);
//缩放文字
float scaleSize = random.nextFloat() + 0.8f;
if(scaleSize > 1f) scaleSize = 1f;
trans.scale(scaleSize, scaleSize);
g2d_word.setTransform(trans);
g.drawString(ctmp, 15 * i + 18, 14);
}
//将生成的验证码保存到Session中
HttpSession session = request.getSession(true);
session.setAttribute("randCheckCode", sRand);
//释放g所占用的系统资源
g.dispose();
//输出图片
ImageIO.write(image, "JPEG", response.getOutputStream());
}
}
2.配置Servlet
在web.xml中的配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name></display-name>
<servlet>
<description>输出验证码</description>
<display-name>This is the display name of my J2EE component</display-name>
<servlet-name>PictureCheckCode</servlet-name>
<servlet-class>com.servlet.PictureCheckCode</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>PictureCheckCode</servlet-name>
<url-pattern>/picturecheckcode</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
3.测试验证码
可以编写JSP页面来验证是否可以输出验证码图片,JSP代码如下:
1.index.jsp:显示界面
1. <%@ page language="java" import="java.util.*" pageEncoding="gbk"%>
2.
3. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
4. <html>
5. <head>
6. <title>验证码</title>
7. "javascript">
8. function myReload() {
9. "CreateCheckCode").src = document
10. "CreateCheckCode").src
11. "?nocache=" + new
12. }
13. </script>
14. </head>
15.
16. <body>
17. "Check.jsp" method="post">
18. "checkCode" type="text" id="checkCode" title="验证码区分大小写"
19. "8" ,maxlength="4"
20. "PictureCheckCode" id="CreateCheckCode" align="middle">
21. "" οnclick="myReload()"> 看不清,换一个</a>
22. "submit" value="提交"
23. </form>
24. </body>
25. </html>
2.Check.jsp :主要验证提交的数据是否和Session中保存的验证码是否相同
1. <%@ page language="java" import="java.util.*" pageEncoding="gbk"%>
2. <html>
3. <head>
4. <title>验证码校验</title>
5. </head>
6.
7. <body>
8. <%
9. "checkCode");
10. if(checkcode.equals("")||checkcode==null){
11. "<script>alert('请输入验证码');window.location.href('index.jsp')</script>");
12. else{
13. if(!checkcode.equalsIgnoreCase((String)session.getAttribute("randCheckCode"))){
14. "<script>alert('验证码不正确,请重新输入');history.back(-1);</script>");
15. else{
16. "登录成功");
17. }
18. }
19. %>
20. </body>
21. </html>
3.工程项目结构,及运行截图