图形验证码工具类封装
通常,在网站开发时,在登录、注册以及一些敏感操作的时候,为了防止服务器被暴力请求,或爬虫爬取,可以使用验证码进行过滤,减轻服务器的压力。
现在常见的验证码技术有:短信验证码、邮箱验证码、图形验证码等,例如:要求输入随机字符序列、数字计算结果、图片选择验证、拼图互动验证等等。
图像验证码是通过绘图技术实现的,严格意义上来说:是在服务器端通过调用画笔对象,将一系列“字符串+干扰图形”绘制到某个Image图像实例上,并将该图像通过IO流通道返还给前端页面显示实现的。当然也可以完全基于前端的canvas绘图实现。
Java图像验证码的相关实现类
对于Java编程语言来说,图形验证码图片资源的创建需要主要借助Image类(图像)、Font字体、Color颜色类、Graphics类()、IO流类实现。
Image抽象类与图像坐标系
Image抽象类是所有表示图形图像的父类,并且图像的底层创建是与系统平台相关的。Image抽象类的继承结构如下图所示。
Image图像坐标系的原点在左上角,这一点可参见:图像像素值处理。
The abstract class Image is the superclass of all classes that represent graphical
images. The image must be obtained in a platform-specific manner.
BufferedImage类
BufferedImage类表示一个带有可访问图像数据缓冲区的图像,该对象由ColorModel和图像数据Raster两个组件组成。
ColorModel组件
①ColorModel组件:用于存储图像的像素数据,该类封装了将像素值转换为颜色分量(例如:R、G、B)和alpha透明度分量的方法。为了将一个Image图像渲染到屏幕、打印机等显示设备,像素值必须被转换为颜色和透明度值,而ColorModel作为这些方法的参数或返回值。
The ColorModel abstract class encapsulates the methods for translating a pixel value to color components (for example, red, green, and blue) and an alpha component.
Raster组件
②Raster是表示矩形像素数组的类,用于存储图像的颜色值数据。该类封装了一个用于存储颜色样本的DataBuffer、一个描述在DataBuffer中定位给定样本值的SampleModel对象。
那么,如何理解这个矩形像素数组呢?
由于像素点的位置是通过坐标值唯一标识的,所示这个矩形像素数组可以理解为一个包含全部像素点的平面区域(或者最小外接矩形),它是由minX、minY、、宽度、高度所定义的Rectangle矩形类对象,可以通过调用getBounds()方法获得,其中:minX、minY对应对应坐标系的原点(左上角的(0,0)位置),也就是接下来我们要通过BufferedImage定义的图像原点和宽高值。
BufferedImage类的基本操作
该类的基本操作,可以参考另一位博主的文章:《BufferedImage 学习》,写的很详细。
Graphics抽象类
Graphics类是所有图形上下文的抽象基类,允许应用程序绘制到各种设备上实现的组件上,也可以绘制到屏外图像上。此次要实现的图形验证码就属于后者。
由于Graphics是一个抽象类,无法直接创建对象,但是可以通过Image对象的getGraphics()方法获取一个对象。
通过Graphics对象,可以绘制2D或者3D对象,到输出设备/图片上。这些可绘制的对象包括:拥有特定字体(通过Font类定义)、特定颜色(通过Color类定义)的文字、图形图形等。
Java图像验证码工具类
这部分就不解释了,直接上代码,对于某些方法可以直接查看jdk文档理解。
package com.xwd.util;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;
/**
* @ClassName VerifyCodeUtil---[生成验证码工具]
* @Description: com.xwd.util
* @Auther: xiwd
* @Date: 2022/2/11 - 02 - 11 - 16:47
* @version: 1.0
*/
public class VerifyCodeUtil {
//methods
private static final String originCode="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";//验证码原始字符序列
private static final Random random=new Random();//随机数对象
private static final Color[] colors=new Color[]{Color.WHITE,Color.ORANGE,Color.RED,Color.GREEN};//定制颜色
public static final int type_verifyImage=0;//不带干扰线的验证码图像
public static final int type_verifyImage_withLine = 1;//带干扰线的验证码图像
/**
* 随机生成四位数验证码
* @return 验证码字符序列
*/
public static String generateVerifyCode(){
StringBuilder verifyCode=new StringBuilder();
for (int i = 0; i < 4; i++) {
verifyCode.append(originCode.charAt(random.nextInt(originCode.length())));
}
return verifyCode.toString();
}
/**
* 根据Image尺寸,生成验证码Image对象
* @param width 宽度
* @param height 高度
* @param verifyCode 验证码
*/
public static Image generateVerifyImage(int width, int height, String verifyCode){
Image image=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);//创建图像
Graphics graphics = image.getGraphics();//获取画笔对象
//根据Image图像宽高计算必要参数
int fontSize = width/5;//计算字体大小
int codeStartPos_x = fontSize/2;
int codeStartPos_y = height/2+fontSize/2;
//设置背景填充颜色
graphics.setColor(Color.pink);
graphics.fillRect(0,0,width,height);
//设置字体样式
Font font =new Font("gothic",Font.PLAIN,fontSize);
graphics.setFont(font);
//设置字体颜色
graphics.setColor(Color.blue);
//将验证码写入图像
for (int i = 0; i < 4; i++) {
graphics.drawString(verifyCode.charAt(i)+"",codeStartPos_x+fontSize*i,codeStartPos_y);//填充验证码
}
return image;
}
/**
* 根据Image尺寸,生成带有干扰线的验证码Image对象
* @param width Image宽度
* @param height Image高度
* @param verifyCode 验证码
* @return Image对象
*/
public static Image generateVerifyImageWithLines(int width,int height, String verifyCode){
Image image = generateVerifyImage(width, height,verifyCode);
Graphics graphics = image.getGraphics();//获取画笔对象
//定义坐标变量
int x1,x2,y1,y2;
//添加干扰线
for (int i = 0; i < 30; i++) {
x1 = random.nextInt(width);
x2 = random.nextInt(width);
y1 = random.nextInt(height);
y2 = random.nextInt(height);
graphics.setColor(colors[random.nextInt(colors.length)]);//设置一定范围的随机色
graphics.drawLine(x1,y1,x2,y2);//绘制干扰线
}
//返回Image对象
return image;
}
/**
* 根据参数获取指定类型的验证码Image对象
* 可选值:
* type_verifyImage
* type_verifyImage_withLine
* @param width Image宽度
* @param height Image高度
* @param verifyCode 验证码
* @param type_image Image类型
* @return RenderedImage对象
*/
public static RenderedImage getVerifyImage(int width,int height,String verifyCode,int type_image){
switch (type_image){
case type_verifyImage:{
return (RenderedImage) generateVerifyImage(width, height,verifyCode);
}
case type_verifyImage_withLine:{
return (RenderedImage) generateVerifyImageWithLines(width, height,verifyCode);
}
default:
return null;
}
}
/**
* 输出指定类型的Image验证码图像到指定流中
* @param width Image宽度
* @param height Image高度
* @param verifyCode 验证码
* @param outputStream 输出流对象
* @return 是否执行成功-true-success;false-failed
* @throws IOException
*/
public static boolean genereteVerifyCodeImage(int width, int height,String verifyCode,int type_image, OutputStream outputStream) throws IOException {
//输出图片
return ImageIO.write(getVerifyImage(width, height, verifyCode, type_image),"jpg",outputStream);
}
/**
* 输出指定类型的Image验证码图像到指定流中——图像尺寸:100*50
* @param outputStream 输出流对象
* @return 是否执行成功-true-success;false-failed
* @throws IOException
*/
public static boolean genereteVerifyCodeImage(String verifyCode,int type_image, OutputStream outputStream) throws IOException {
//输出图片
return ImageIO.write(getVerifyImage(100, 50,verifyCode, type_image),"jpg",outputStream);
}
}
验证码工具类&Servlet配置使用
定义Servlet类
package com.xwd.servlet;
import com.xwd.util.VerifyCodeUtil;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @ClassName VerifyCodeServlet
* @Description: com.xwd.servlet
* @Auther: xiwd
* @Date: 2022/2/11 - 02 - 11 - 16:07
* @version: 1.0
*/
@WebServlet(
name = "verifycode",
value = {
"/verifycode"
}
)
public class VerifyCodeServlet extends HttpServlet {
//methods
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//super.doGet(req, resp);
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//super.doPost(req, resp);
req.setCharacterEncoding("UTF-8");
resp.setContentType("image/jpeg;charset=UTF-8");
boolean flag=false;
//获取验证码
String verifyCode = VerifyCodeUtil.generateVerifyCode();
//获取输出流对象
ServletOutputStream outputStream = resp.getOutputStream();
try{
//获取请求参数
int width = Integer.valueOf(req.getParameter("width"));
int height = Integer.valueOf(req.getParameter("height"));
//返回验证码Image对象
flag = VerifyCodeUtil.genereteVerifyCodeImage(width, height, verifyCode,VerifyCodeUtil.type_verifyImage_withLine, outputStream);
}catch (Exception e){
//自动输出宽100,高50的图像
flag = VerifyCodeUtil.genereteVerifyCodeImage(verifyCode,VerifyCodeUtil.type_verifyImage_withLine, outputStream);
}
if (flag){
System.out.println("SUCCESS!");
}else
System.out.println("FAILED!");
}
}
测试
(1)返回默认宽高100*50的图像验证码图像,
(2)返回指定宽高120*60的图像验证码图像,