目录
前言
在自己做的某一个项目中要求具备滑块验证码的功能,在翻阅部分博客后自己参考编写了一个后台的滑块验证码工具类,这里要给提供参考资料的博客说声谢谢,但是因时间久远忘记了之前的博客地址,所以也要说声抱歉。
思路流程
验证码的生成及验证流程是利用jhlabs jar包工具对图片进行抠图,将抠出的图片与背景图片通过base64编码转转成图片字符串发送给前台,并记录抠出图片的X坐标值,后续校验过程中我们会根据前台用户滑动的X坐标值与我们后台记录的X坐标值进行比较,在坐标值范围内则可证明验证成功。
注意点:
1.背景图使我们提前准备好的静态资源,图片像素大小为需均一致,并且前台编写时也要根据像素定义好验证码的长和高,这里默认设置了350X213像素的大小,后续更改图片大小也需要更改工具类中的宽和高。
2.在编译使用加载静态资源背景图时通过idea运行与直接运行jar包中的资源地址是不同的,这里我编写了jar包静态资源的加载函数,使用时可根据实际地址进行修改。
3.这里的验证码存储使用的是Ehcache,可根据自己的缓存技术修改存储。
工具类代码
pom
<dependency> <groupId>com.jhlabs</groupId> <artifactId>filters</artifactId> <version>2.0.235</version> </dependency>
<!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all --> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.2.3</version> </dependency>
抠图工具类
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.resource.Resource;
import cn.hutool.core.util.NumberUtil;
import com.jhlabs.image.ImageUtils;
import com.jhlabs.image.InvertAlphaFilter;
import com.jhlabs.image.ShadowFilter;
import lombok.Data;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.geom.Arc2D;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.Random;
/**
* 滑块验证码
*
* @author hexm
* @date 2020/10/23
*/
@Data
public class PuzzleCaptcha {
/** 默认宽度,用来计算阴影基本长度 */
private static final int DEFAULT_WIDTH = 350;
/** 随机数 */
private static final Random RANDOM = new Random();
/** 蒙版 */
private static Color color = new Color(255, 255, 255, 204);
/** alpha通道过滤器 */
private static InvertAlphaFilter alphaFilter = new InvertAlphaFilter();
/** 边距 */
private static int margin = 0;
/** 生成图片的宽度 */
private int width = DEFAULT_WIDTH;
/** 生成图片高度 */
private int height = 213;
/** x轴的坐标,由算法决定 */
private int x;
/** y轴的坐标,由算法决定 */
private int y;
/** 拼图长宽 */
private int vwh = 10 * 3;
/** 原图 */
private Image image;
/** 大图 */
private Image artwork;
/** 小图 */
private Image vacancy;
/** 是否注重速度 */
private boolean isFast = false;
/** 小图描边颜色 */
private Color vacancyBorderColor;
/** 小图描边线条的宽度 */
private float vacancyBorderWidth = 2.5f;
/** 主图描边的颜色 */
private Color artworkBorderColor;
/** 主图描边线条的宽度 */
private float artworkBorderWidth = 5f;
/**
* 最高放大倍数,合理的放大倍数可以使图像平滑且提高渲染速度
* 当isFast为false时,此属性生效
* 放大倍数越高,生成的图像越平滑,受原始图片大小的影响。
*/
private double maxRatio = 2;
/**
* 画质
*
* @see Image#SCALE_DEFAULT
* @see Image#SCALE_FAST
* @see Image#SCALE_SMOOTH
* @see Image#SCALE_REPLICATE
* @see Image#SCALE_AREA_AVERAGING
*/
private int imageQuality = Image.SCALE_SMOOTH;
/**
* 从文件中读取图片
*
* @param file
*/
public PuzzleCaptcha(File file) {
image = ImgUtil.read(file);
}
/**
* 从文件中读取图片,请使用绝对路径,使用相对路径会相对于ClassPath
*
* @param imageFilePath
*/
public PuzzleCaptcha(String imageFilePath) {
image = ImgUtil.read(imageFilePath);
}
/**
* 从{@link Resource}中读取图片
*
* @param resource
*/
public PuzzleCaptcha(Resource resource) {
image = ImgUtil.read(resource);
}
/**
* 从流中读取图片
*
* @param imageStream
*/
public PuzzleCaptcha(InputStream imageStream) {
image = ImgUtil.read(imageStream);
}
/**
* 从图片流中读取图片
*
* @param imageStream
*/
public PuzzleCaptcha(ImageInputStream imageStream) {
image = ImgUtil.read(imageStream);
}
/**
* 加载图片
*
* @param image
*/
public PuzzleCaptcha(Image image) {
this.image = image;
}
/**
* 加载图片
*
* @param bytes
*/
public PuzzleCaptcha(byte[] bytes) {
this.image = ImgUtil.read(new ByteArrayInputStream(bytes));
}
/**
* 生成随机x、y坐标
*/
private void init() {
if (x == 0 || y == 0) {
this.x = random(vwh, this.width - vwh - margin);
this.y = random(margin, this.height - vwh - margin);
}
}
/**
* 执行
*/
public void run() {
init();
// 缩略图
Image thumbnail;
GeneralPath path;
int realW = image.getWidth(null);
int realH = image.getHeight(null);
int w = realW, h = realH;
double wScale = 1, hScale = 1;
// 如果原始图片比执行的图片还小,则先拉伸再裁剪
boolean isFast = this.isFast || w < this.width || h < this.height;
if (isFast) {
// 缩放,使用平滑模式
thumbnail = image.getScaledInstance(width, height, imageQuality);
path = paintBrick(1, 1);
w = this.width;
h = this.height;
} else {
// 缩小到一定的宽高,保证裁剪的圆润
boolean flag = false;
if (realW > width * maxRatio) {
// 不超过最大倍数且不超过原始图片的宽
w = Math.min((int) (width * maxRatio), realW);
flag = true;
}
if (realH > height * maxRatio) {
h = Math.min((int) (height * maxRatio), realH);
flag = true;
}
if (flag) {
// 若放大倍数生效,则缩小图片至最高放大倍数,再进行裁剪
thumbnail = image.getScaledInstance(w, h, imageQuality);
} else {
thumbnail = image;
}
hScale = NumberUtil.div(h, height);