|
|
|
@ -24,93 +24,129 @@ import org.patchca.utils.encoder.EncoderHelper;
|
|
|
|
|
import org.patchca.word.RandomWordFactory;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 验证码工具
|
|
|
|
|
* 验证码工具类,用于生成带有各种样式和效果的验证码图片,并返回对应的验证码字符内容。
|
|
|
|
|
* 该类基于Patchca库来实现验证码的生成功能,通过配置不同的属性来定制验证码的样式、字体、背景等元素。
|
|
|
|
|
* @author ThinkGem
|
|
|
|
|
* @version 2017年12月23日
|
|
|
|
|
*/
|
|
|
|
|
public class CaptchaUtils {
|
|
|
|
|
|
|
|
|
|
// 用于生成随机数,在验证码生成过程中多处用于随机取值,比如字体颜色、噪点位置等的随机化处理
|
|
|
|
|
private static Random random = new Random();
|
|
|
|
|
// 可配置的验证码服务对象,用于设置和管理验证码生成的各项参数及执行生成操作
|
|
|
|
|
private static ConfigurableCaptchaService ccs;
|
|
|
|
|
private static WobbleRippleFilterFactory wrff; // 摆波纹
|
|
|
|
|
private static DoubleRippleFilterFactory doff; // 双波纹
|
|
|
|
|
private static CurvesRippleFilterFactory crff; // 曲线波纹
|
|
|
|
|
private static DiffuseRippleFilterFactory drff; // 漫纹波
|
|
|
|
|
private static MarbleRippleFilterFactory mrff; // 大理石
|
|
|
|
|
|
|
|
|
|
private static void initialize(){
|
|
|
|
|
if (ccs == null){
|
|
|
|
|
synchronized (CaptchaUtils.class) {
|
|
|
|
|
if (ccs == null){
|
|
|
|
|
// 配置初始化
|
|
|
|
|
ccs = new ConfigurableCaptchaService();
|
|
|
|
|
|
|
|
|
|
// 设置图片大小
|
|
|
|
|
ccs.setWidth(100);
|
|
|
|
|
ccs.setHeight(28);
|
|
|
|
|
|
|
|
|
|
// 设置文字数量
|
|
|
|
|
RandomWordFactory wf = new RandomWordFactory();
|
|
|
|
|
wf.setCharacters("ABDEFGHKMNRSWX2345689");
|
|
|
|
|
wf.setMinLength(4);
|
|
|
|
|
wf.setMaxLength(4);
|
|
|
|
|
ccs.setWordFactory(wf);
|
|
|
|
|
|
|
|
|
|
// 设置字体大小
|
|
|
|
|
RandomFontFactory ff = new RandomFontFactory();
|
|
|
|
|
ff.setMinSize(28);
|
|
|
|
|
ff.setMaxSize(28);
|
|
|
|
|
ccs.setFontFactory(ff);
|
|
|
|
|
|
|
|
|
|
// 设置文字渲染边距
|
|
|
|
|
BestFitTextRenderer tr = new BestFitTextRenderer();
|
|
|
|
|
tr.setTopMargin(3);
|
|
|
|
|
tr.setRightMargin(3);
|
|
|
|
|
tr.setBottomMargin(3);
|
|
|
|
|
tr.setLeftMargin(3);
|
|
|
|
|
// 摆波纹滤镜工厂对象,用于创建摆波纹效果的滤镜,应用在验证码图片上以增加干扰效果和美观度
|
|
|
|
|
private static WobbleRippleFilterFactory wrff;
|
|
|
|
|
// 双波纹滤镜工厂对象,类似地,用于生成双波纹效果的滤镜
|
|
|
|
|
private static DoubleRippleFilterFactory doff;
|
|
|
|
|
// 曲线波纹滤镜工厂对象,用于生成曲线波纹效果的滤镜
|
|
|
|
|
private static CurvesRippleFilterFactory crff;
|
|
|
|
|
// 漫纹波滤镜工厂对象,用于生成漫纹波效果的滤镜
|
|
|
|
|
private static DiffuseRippleFilterFactory drff;
|
|
|
|
|
// 大理石滤镜工厂对象,用于生成大理石纹理效果的滤镜
|
|
|
|
|
private static MarbleRippleFilterFactory mrff;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 初始化方法,用于初始化验证码生成相关的配置和资源,采用了双重检查锁定(Double-Checked Locking)的单例模式来确保ConfigurableCaptchaService对象只被初始化一次。
|
|
|
|
|
* 在多线程环境下,保证只有一个线程进行初始化操作,其他线程等待初始化完成后使用。
|
|
|
|
|
*/
|
|
|
|
|
private static void initialize() {
|
|
|
|
|
if (ccs == null) {
|
|
|
|
|
// 使用类级别的锁,确保在多线程环境下对初始化代码块的互斥访问,防止多次初始化
|
|
|
|
|
synchronized (CaptchaUtils.class) {
|
|
|
|
|
if (ccs == null) {
|
|
|
|
|
// 实例化ConfigurableCaptchaService对象,用于后续配置和生成验证码
|
|
|
|
|
ccs = new ConfigurableCaptchaService();
|
|
|
|
|
|
|
|
|
|
// 设置验证码图片的宽度和高度,单位为像素,这里设置宽度为100像素,高度为28像素
|
|
|
|
|
ccs.setWidth(100);
|
|
|
|
|
ccs.setHeight(28);
|
|
|
|
|
|
|
|
|
|
// 配置随机文字生成工厂,用于确定验证码中出现的字符范围、长度等信息
|
|
|
|
|
RandomWordFactory wf = new RandomWordFactory();
|
|
|
|
|
// 设置验证码文字可使用的字符集合,排除了容易混淆的字符,提高验证码的辨识度
|
|
|
|
|
wf.setCharacters("ABDEFGHKMNRSWX2345689");
|
|
|
|
|
// 设置验证码文字的最小长度为4个字符
|
|
|
|
|
wf.setMinLength(4);
|
|
|
|
|
// 设置验证码文字的最大长度也为4个字符,保证生成的验证码长度固定为4
|
|
|
|
|
wf.setMaxLength(4);
|
|
|
|
|
ccs.setWordFactory(wf);
|
|
|
|
|
|
|
|
|
|
// 配置随机字体生成工厂,用于设置验证码文字的字体大小范围
|
|
|
|
|
RandomFontFactory ff = new RandomFontFactory();
|
|
|
|
|
// 设置字体的最小尺寸为28像素,确保字体大小合适且相对统一
|
|
|
|
|
ff.setMinSize(28);
|
|
|
|
|
// 设置字体的最大尺寸为28像素,与最小尺寸相同,保证字体大小固定
|
|
|
|
|
ff.setMaxSize(28);
|
|
|
|
|
ccs.setFontFactory(ff);
|
|
|
|
|
|
|
|
|
|
// 配置文字渲染器,用于设置验证码文字在图片上的边距,确保文字显示位置合适且美观
|
|
|
|
|
BestFitTextRenderer tr = new BestFitTextRenderer();
|
|
|
|
|
// 设置文字距离图片顶部的边距为3像素
|
|
|
|
|
tr.setTopMargin(3);
|
|
|
|
|
// 设置文字距离图片右侧的边距为3像素
|
|
|
|
|
tr.setRightMargin(3);
|
|
|
|
|
// 设置文字距离图片底部的边距为3像素
|
|
|
|
|
tr.setBottomMargin(3);
|
|
|
|
|
// 设置文字距离图片左侧的边距为3像素
|
|
|
|
|
tr.setLeftMargin(3);
|
|
|
|
|
ccs.setTextRenderer(tr);
|
|
|
|
|
|
|
|
|
|
// 设置字体颜色
|
|
|
|
|
ccs.setColorFactory(new ColorFactory() {
|
|
|
|
|
@Override
|
|
|
|
|
public Color getColor(int x) {
|
|
|
|
|
int r = random.nextInt(90);
|
|
|
|
|
int g = random.nextInt(90);
|
|
|
|
|
int b = random.nextInt(90);
|
|
|
|
|
return new Color(r, g, b);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 设置背景
|
|
|
|
|
ccs.setBackgroundFactory(new BackgroundFactory() {
|
|
|
|
|
// 配置字体颜色工厂,用于随机生成验证码文字的颜色,颜色取值范围在较浅的色彩区间内,以保证对比度和可读性
|
|
|
|
|
ccs.setColorFactory(new ColorFactory() {
|
|
|
|
|
@Override
|
|
|
|
|
public Color getColor(int x) {
|
|
|
|
|
// 随机生成红色分量,范围在0 - 90之间,使得颜色相对较浅
|
|
|
|
|
int r = random.nextInt(90);
|
|
|
|
|
// 随机生成绿色分量,范围在0 - 90之间,同样保证较浅的颜色
|
|
|
|
|
int g = random.nextInt(90);
|
|
|
|
|
// 随机生成蓝色分量,范围在0 - 90之间
|
|
|
|
|
int b = random.nextInt(90);
|
|
|
|
|
return new Color(r, g, b);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 配置背景工厂,用于绘制验证码图片的背景,包括填充背景颜色、添加噪点和干扰线等操作,以增加验证码的复杂性和安全性
|
|
|
|
|
ccs.setBackgroundFactory(new BackgroundFactory() {
|
|
|
|
|
@Override
|
|
|
|
|
public void fillBackground(BufferedImage image) {
|
|
|
|
|
// 获取图片的图形上下文对象,用于在图片上进行绘制操作
|
|
|
|
|
Graphics graphics = image.getGraphics();
|
|
|
|
|
// 验证码图片的宽高
|
|
|
|
|
// 获取验证码图片的宽度,单位为像素
|
|
|
|
|
int imgWidth = image.getWidth();
|
|
|
|
|
// 获取验证码图片的高度,单位为像素
|
|
|
|
|
int imgHeight = image.getHeight();
|
|
|
|
|
// 填充为白色背景
|
|
|
|
|
// 设置填充颜色为白色,用于将整个验证码图片背景填充为白色
|
|
|
|
|
graphics.setColor(Color.WHITE);
|
|
|
|
|
// 使用白色填充整个图片区域,左上角坐标为(0, 0),宽度和高度为图片的实际宽高
|
|
|
|
|
graphics.fillRect(0, 0, imgWidth, imgHeight);
|
|
|
|
|
// 画 50 个噪点(颜色及位置随机)
|
|
|
|
|
|
|
|
|
|
// 循环绘制50个噪点,噪点的颜色、位置、旋转角度和大小都是随机生成的,增加验证码的干扰性和安全性
|
|
|
|
|
for (int i = 0; i < 50; i++) {
|
|
|
|
|
// 随机颜色
|
|
|
|
|
int rInt = random.nextInt(100)+50;
|
|
|
|
|
int gInt = random.nextInt(100)+50;
|
|
|
|
|
int bInt = random.nextInt(100)+50;
|
|
|
|
|
// 随机生成红色分量,范围在50 - 150之间,使得噪点颜色不过于浅或深,保证一定的辨识度
|
|
|
|
|
int rInt = random.nextInt(100) + 50;
|
|
|
|
|
// 随机生成绿色分量,范围在50 - 150之间
|
|
|
|
|
int gInt = random.nextInt(100) + 50;
|
|
|
|
|
// 随机生成蓝色分量,范围在50 - 150之间
|
|
|
|
|
int bInt = random.nextInt(100) + 50;
|
|
|
|
|
graphics.setColor(new Color(rInt, gInt, bInt));
|
|
|
|
|
// 随机位置
|
|
|
|
|
|
|
|
|
|
// 随机生成噪点的x坐标,范围在0到图片宽度减3之间,避免超出图片边界
|
|
|
|
|
int xInt = random.nextInt(imgWidth - 3);
|
|
|
|
|
// 随机生成噪点的y坐标,范围在0到图片高度减2之间,同样避免超出边界
|
|
|
|
|
int yInt = random.nextInt(imgHeight - 2);
|
|
|
|
|
// 随机旋转角度
|
|
|
|
|
// 随机生成噪点的起始旋转角度,范围在0 - 360度之间,增加噪点的随机性
|
|
|
|
|
int sAngleInt = random.nextInt(360);
|
|
|
|
|
// 随机生成噪点的结束旋转角度,范围在0 - 360度之间
|
|
|
|
|
int eAngleInt = random.nextInt(360);
|
|
|
|
|
// 随机大小
|
|
|
|
|
// 随机生成噪点的宽度,范围在0 - 6像素之间,控制噪点大小
|
|
|
|
|
int wInt = random.nextInt(6);
|
|
|
|
|
// 随机生成噪点的高度,范围在0 - 6像素之间
|
|
|
|
|
int hInt = random.nextInt(6);
|
|
|
|
|
// 填充背景
|
|
|
|
|
// 使用生成的参数绘制一个填充的弧形(可以看作是不规则形状的噪点),起始角度和结束角度、宽度和高度共同决定了弧形的形状和大小
|
|
|
|
|
graphics.fillArc(xInt, yInt, wInt, hInt, sAngleInt, eAngleInt);
|
|
|
|
|
// 画5条干扰线
|
|
|
|
|
|
|
|
|
|
// 每间隔10个噪点(即i % 10 == 0时),绘制一条干扰线,干扰线的起点是当前噪点位置,终点是图片内的随机位置,进一步增加验证码的干扰性
|
|
|
|
|
if (i % 10 == 0) {
|
|
|
|
|
int xInt2 = random.nextInt(imgWidth);
|
|
|
|
|
int yInt2 = random.nextInt(imgHeight);
|
|
|
|
@ -120,62 +156,60 @@ public class CaptchaUtils {
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 效果初始化
|
|
|
|
|
wrff = new WobbleRippleFilterFactory(); // 摆波纹
|
|
|
|
|
doff = new DoubleRippleFilterFactory(); // 双波纹
|
|
|
|
|
crff = new CurvesRippleFilterFactory(ccs.getColorFactory()); // 曲线波纹
|
|
|
|
|
drff = new DiffuseRippleFilterFactory(); // 漫纹波
|
|
|
|
|
mrff = new MarbleRippleFilterFactory(); // 大理石
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
// 初始化各种滤镜工厂对象,用于后续根据随机选择为验证码图片应用不同的滤镜效果
|
|
|
|
|
wrff = new WobbleRippleFilterFactory();
|
|
|
|
|
doff = new DoubleRippleFilterFactory();
|
|
|
|
|
crff = new CurvesRippleFilterFactory(ccs.getColorFactory());
|
|
|
|
|
drff = new DiffuseRippleFilterFactory();
|
|
|
|
|
mrff = new MarbleRippleFilterFactory();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 生成验证码
|
|
|
|
|
* @param request
|
|
|
|
|
* @param response
|
|
|
|
|
* @throws IOException
|
|
|
|
|
* @return 验证码字符
|
|
|
|
|
* 生成验证码的方法,通过配置好的ConfigurableCaptchaService对象生成验证码图片,并将其以PNG格式输出到指定的输出流中,同时返回验证码对应的字符内容。
|
|
|
|
|
*
|
|
|
|
|
* @param outputStream 用于输出验证码图片数据的输出流,通常可以是HttpServletResponse的输出流或者文件输出流等,将验证码图片以PNG格式写入该流。
|
|
|
|
|
* @throws IOException 如果在生成验证码图片或者向输出流写入数据过程中出现I/O异常,则抛出该异常。
|
|
|
|
|
* @return 返回生成的验证码字符内容,即图片中显示的文本信息,供后续验证等操作使用。
|
|
|
|
|
*/
|
|
|
|
|
public static String generateCaptcha(OutputStream outputStream) throws IOException{
|
|
|
|
|
|
|
|
|
|
// 初始化设置
|
|
|
|
|
public static String generateCaptcha(OutputStream outputStream) throws IOException {
|
|
|
|
|
// 调用初始化方法,确保验证码生成相关的配置和资源已正确初始化
|
|
|
|
|
initialize();
|
|
|
|
|
|
|
|
|
|
// 随机选择一个样式
|
|
|
|
|
switch (random.nextInt(3)) {
|
|
|
|
|
case 0:
|
|
|
|
|
ccs.setFilterFactory(wrff); // 摆波纹
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
ccs.setFilterFactory(doff); // 双波纹
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
ccs.setFilterFactory(crff); // 曲线波纹
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
ccs.setFilterFactory(drff); // 漫纹波
|
|
|
|
|
break;
|
|
|
|
|
case 4:
|
|
|
|
|
ccs.setFilterFactory(mrff); // 大理石
|
|
|
|
|
break;
|
|
|
|
|
// 随机选择一个滤镜样式(共5种可选),并将对应的滤镜工厂设置到ConfigurableCaptchaService中,用于为验证码图片添加相应的滤镜效果
|
|
|
|
|
switch (random.nextInt(5)) {
|
|
|
|
|
case 0:
|
|
|
|
|
ccs.setFilterFactory(wrff); // 摆波纹
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
ccs.setFilterFactory(doff); // 双波纹
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
ccs.setFilterFactory(crff); // 曲线波纹
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
ccs.setFilterFactory(drff); // 漫纹波
|
|
|
|
|
break;
|
|
|
|
|
case 4:
|
|
|
|
|
ccs.setFilterFactory(mrff); // 大理石
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 生成验证码
|
|
|
|
|
String s = EncoderHelper.getChallangeAndWriteImage(ccs, "png", outputStream);
|
|
|
|
|
// 使用EncoderHelper工具类,结合已配置好的ConfigurableCaptchaService对象,生成验证码图片并将其以PNG格式写入指定的输出流中,同时返回验证码的字符内容
|
|
|
|
|
String s = EncoderHelper.getChallangeAndWriteImage(ccs, "png", outputStream);
|
|
|
|
|
// System.out.println(s);
|
|
|
|
|
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// public static void main(String[] args) throws IOException {
|
|
|
|
|
// public static void main(String[] args) throws IOException {
|
|
|
|
|
//
|
|
|
|
|
// FileOutputStream fos = new FileOutputStream("x:\\captcha.png");
|
|
|
|
|
// String s = generateCaptcha(fos);
|
|
|
|
|
// System.out.println(s);
|
|
|
|
|
// fos.close();
|
|
|
|
|
// FileOutputStream fos = new FileOutputStream("x:\\captcha.png");
|
|
|
|
|
// String s = generateCaptcha(fos);
|
|
|
|
|
// System.out.println(s);
|
|
|
|
|
// fos.close();
|
|
|
|
|
//
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
}
|