java实现滑动验证码
准备⼯作
1、若⼲原图⽚(下⽂称为原图)
2、⼀张抠图形状图⽚(下⽂称为滑块模板,抠出来的图称为滑块)
3、⼀张抠图边框图⽚(下⽂称为滑块边框)
注意:滑块模板尺⼨必须⼩于原图尺⼨
实现思路
1、后端 - 在若⼲原图中随机⼀张原图
2、后端 - 根据滑块模板随机在原图中随机⼀个矩形区域,并且得到区域x、y坐标,注意随机区域不要超过原图边界
3、后端 - 根据滑块模板在原图上抠图(原理:滑块模板中滑块是有颜⾊的,根据滑块模板坐标可以直接取到原图坐标,然后在取原图坐标点颜⾊并赋值到滑块)
4、后端 - 根据滑块模板在原图上设置遮罩缺块(原理:同上取到原图坐标并取得原图⾊值,把⾊值调低并放回去就形成遮罩缺块)
5、后端 - 为了⽤户体验给滑块加边框(原理:滑块边框与滑块模板同等画布,且边框刚好⼤于滑块,直接取边框中有颜⾊的坐标,并把颜⾊设置在滑块上)
6、后端 - 把滑块图⽚Base64,有缺块的原图Base64
7、后端 - 经过前6步已经可以得到,两个Base64的字符串和⼀个坐标x、y,随机⼀个key,把坐标x、y存在redis中
8、后端 - 返回前端两个Base64的字符串和y坐标 和key
9、前端 - 每隔100毫秒获取⼀次滑块坐标,滑动结束后把key和路径坐标数组返回给后端
10、后端 - 得到key和路径数组后,从redis中取出缺块坐标,⽤数组中最后⼀个坐标与缺块坐标⽐较,差值是否在预设差值范围内,如果不在直接验证失败
11、(可不选)后端 - 为防⽌机器学习模拟滑块滑动,可以做路径校验,校验原理开始慢,中间快,最后慢(需要慢慢调法值,这⾥省略)
滑块模板
滑块边框
背景图
代码:
public class ImageSlideVerification {
/**
* 源图⽬录路径
*/
private static String ORIGIN_CATALOG = "original";
/**
* 模板图⽬录路径
*/
private static String TEMPLATE_CATALOG = "templates";
/**
* 描边图⽬录路径
*/
private static String BORDER_CATALOG = "templates";
/**
* Image-key
*/
private static String IMAGE_KEY = "IMAGE:KEY:";
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* 获取滑动验证码
* @return
*/
public SlideVerificationVO getSlideCode() {
//  原图
File originalFile = getOriginalFile();
//  滑块模板图
File templateFile = getTemplateFile();
//  滑块描边图⽚
File borderFile = getBorderFile();
// ⽣成随机x、y
int x = generateX(originalFile, templateFile);
int y = generateY(originalFile, templateFile);
// ⽣成滑块
String slide = cutSlideImage(originalFile, templateFile, borderFile, x, y);
// ⽣成缺块图
String back = cutBackImage(originalFile, templateFile, borderFile, x, y);
// ⽣成可以
String imageToken = UUID.randomUUID().toString().replace("-", "");
// 放⼊redis
Map<String, Integer> cacheMap = new HashMap<>();
cacheMap.put("x", x);
cacheMap.put("y", y);
redisTemplate.opsForValue().set(IMAGE_KEY + imageToken, JSONString(cacheMap), 5, TimeUnit.MINUTES);
SlideVerificationVO vo = new SlideVerificationVO();
vo.setImageToken(imageToken);
vo.setBackImage(back);
vo.setSlideImage(slide);
vo.setY(y + "");
return vo;
}
/**
* 验证滑动验证码
* @param imageToken
* @param imageCode 数据结构 [{x:0,y:0},{x:10,y:10},{x:20,y:20}]
* @return
*/
private boolean checkSlideCode(String imageToken, String imageCode) {
/
/ redis 取值
String json = redisTemplate.opsForValue().get(IMAGE_KEY + imageToken);
if (StringUtils.isEmpty(json)) {
return false;
}
// 获取缓存中的x、y
Map<String, Integer> cacheMap = JSONObject.parseObject(json, Map.class);
int x = ("x");
int y = ("y");
// 获取参数中最后⼀个元素
List<Map> imageCodeList = JSONObject.parseArray(imageCode, Map.class);        Map codeMap = (imageCodeList.size() - 1);
Object codeX = ("x");
Object codeY = ("y");
if (codeX == null || codeY == null) {
return false;
}
// 验证 x
int errorX = Integer.String()) - x;
if (errorX > 1 || errorX < -1) {
return false;
}
// 验证 y
int errorY = Integer.String()) - y;
if (errorY > 1 || errorY < -1) {
return false;
}
// *****验证路径*****
// *****验证路径*****
return true;
}
/**
* 获取跟⽬录
* @return
*/
private static String getClassPath() {
return Class().getResource("/").getPath();
}
/**
* 获取随机原图图⽚
*/
private static File getOriginalFile() {
StringBuilder builder = new StringBuilder(getClassPath());
builder.append(ORIGIN_CATALOG);
builder.append("/");
builder.append(new Random().nextInt(10));
builder.append(".png");
return new String());
}
/**
* 滑块模板图⽚
*/
private static File getTemplateFile() {
StringBuilder builder = new StringBuilder(getClassPath());
builder.append(TEMPLATE_CATALOG);
builder.append("/");
builder.append("template.png");
return new String());
}
/**
* 获取描边原图图⽚
*/
private static File getBorderFile() {
StringBuilder builder = new StringBuilder(getClassPath());
builder.append(BORDER_CATALOG);
builder.append("/");
builder.append("border.png");
return new String());
}
/**
* 随机X坐标
* @param originalFile
* @param templateFile
* @return
*/
private static int generateX(File originalFile , File templateFile) {
try {
BufferedImage originalImage = ad(originalFile);
BufferedImage templateImage = ad(templateFile);
//  原图宽度
int width = Width();
//  模板宽度
int templateImageWidth = Width();
Random random = new Random(System.currentTimeMillis());
Int(width - templateImageWidth) % (width - templateImageWidth - templateImageWidth + 1) + templateImageWidth;
} catch (IOException e) {
e.printStackTrace();
}
return 0;
}
/**
* 随机Y坐标
* @param originalFile
* @param templateFile
* @return
*/
private static int generateY(File originalFile , File templateFile) {
try {
BufferedImage originalImage = ad(originalFile);
BufferedImage templateImage = ad(templateFile);
/
/  原图⾼度
int height = Height();
//  抠图模板⾼度
int templateImageHeight = Height();
Random random = new Random(System.currentTimeMillis());
if (templateImageHeight - height >= 0) {
Int(10);
}
Int(height - templateImageHeight) % (height - templateImageHeight - templateImageHeight + 1) + templateImageHeight;        } catch (IOException e) {
e.printStackTrace();
}
return 0;
}
/**
* 裁剪滑块
java源代码加密* @param originalFile
* @param templateFile
* @param borderFile
* @param x
* @param y
* @return
*/
private static String cutSlideImage(File originalFile , File templateFile, File borderFile, int x, int y) {        ImageInputStream originImageInputStream = null;
ByteArrayOutputStream byteArrayOutputStream = null;
try {
BufferedImage templateImage = ad(templateFile);
BufferedImage borderImage = ad(borderFile);
int with = Width();
int height = Height();
//  创建滑块
BufferedImage cutoutImage = new BufferedImage(with, height, Type());
// 从原图中获取与滑块⼀样⼤⼩的4边形图⽚
ImageReader originalReader = ImageReadersByFormatName("png").next();
originImageInputStream = ateImageInputStream(originalFile);
//  图⽚输⼊流顺序读写
originalReader.setInput(originImageInputStream, true);
//  根据坐标⽣成矩形
ImageReadParam imageReadParam = DefaultReadParam();
Rectangle rectangle = new Rectangle(x, y, with, height);
imageReadParam.setSourceRegion(rectangle);
// 从原图中裁剪下来的图⽚
BufferedImage cutImage = ad(0, imageReadParam);
// 获取剪切图⽚矩阵颜⾊
int[][] cutColorArray = getColorArray(cutImage);
/
/ 获取模板图⽚矩阵颜⾊
int[][] templateColorArray = getColorArray(templateImage);
// 获取边框图⽚矩阵颜⾊
int[][] borderColorArray = getColorArray(borderImage);
// 将模板图⾮透明像素设置到剪切图中
for (int i = 0; i < templateColorArray.length; i++) {
for (int j = 0; j < templateColorArray[0].length; j++) {
// 滑块模板颜⾊不为透明时设置剪切图颜⾊
int templateRGB = templateColorArray[i][j];
if (templateRGB != 16777215 && templateRGB < 0) {
cutoutImage.setRGB(i, j, cutColorArray[i][j]);
}
// 设置边框颜⾊
int borderRGB = borderColorArray[i][j];
if (borderRGB != 16777215 && borderRGB < 0) {
int[] colorArray = {(borderColorArray[i][j] & 0xff0000) >> 16,
(borderColorArray[i][j] & 0xff00) >> 8,
(borderColorArray[i][j] & 0xff)};
// 我的边框是⿊⾊所以我这是加
colorArray[0] += (cutColorArray[i][j] & 0xff0000) >> 16;
colorArray[1] += (cutColorArray[i][j] & 0xff00) >> 8;
colorArray[2] += (cutColorArray[i][j] & 0xff);
cutoutImage.setRGB(i, j , new Color(colorArray[0] > 255 ? 255 : colorArray[0],
colorArray[1] > 255 ? 255 : colorArray[1],
colorArray[2] > 255 ? 255 : colorArray[2]).getRGB());
}
}
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。