前言

1、下面是一个效果展示;

2、先抱怨一下,在博客上面的抄袭真的非常严重,为了实现一个图片滑动验证,我搜索了挺久的资料,不过内容翻来覆去就是同样的内容,千篇一律,作者还各不相同;内容相同我就不多说了,毕竟能解决问题就行,然而恰恰相反,这些东西都没有为我实质性地解决问题。可能图片验证是一个需要前后台同时交互的功能吧,从业的人员大部分都是偏向后台或者偏向前台的,所以写出来的博客都不能完整阐述整个流程,下面是我自己实践完成的内容,记录一下,供各位参阅斧正。

注:由于使用到的控件和工具较多,有许多地方做了省略,这里只做核心流程的记录。

一、后端图片裁剪与生成

首先是一个图片处理工具VerifyImageUtil.class,它主要的作用是生成两张图片:一张被扣除了一部分的原始图片;一张抠出来图片。两两结合,可以组成一张完整的图片。原始图片(target目录)提供了20张,规格都是590*360的;抠图需要的模板图(template目录)有4张,规格都是93*360的(图片等各种资源会在文末给出)。将图片资源导入到我们项目的静态资源路径下(你也可以通过其他方式存储它们),我这边是Spring Boot的项目,所以就放在resource下的static目录下了:

下面是 VerifyImageUtil.class

package com.mine.risk.util;

import org.apache.commons.lang.StringUtils;

import javax.imageio.ImageIO;

import javax.imageio.ImageReadParam;

import javax.imageio.ImageReader;

import javax.imageio.stream.ImageInputStream;

import java.awt.*;

import java.awt.image.BufferedImage;

import java.io.ByteArrayOutputStream;

import java.io.File;

import java.io.FileInputStream;

import java.io.InputStream;

import java.text.NumberFormat;

import java.util.HashMap;

import java.util.Iterator;

import java.util.Map;

import java.util.Random;

/**

* 滑块验证工具类

* @author : spirit

* @date : Created in 10:57 2019/9/05

*/

public class VerifyImageUtil {

/** 源文件宽度 */

private static final int ORI_WIDTH = 590;

/** 源文件高度 */

private static final int ORI_HEIGHT = 360;

/** 抠图坐标x */

private static int X;

/** 抠图坐标y */

private static int Y;

/** 模板图宽度 */

private static int WIDTH;

/** 模板图高度 */

private static int HEIGHT;

public static int getX() {

return X;

}

public static int getY() {

return Y;

}

/**

* 根据模板切图

* @param templateFile 模板文件

* @param targetFile 目标文件

* @param templateType 模板文件类型

* @param targetType 目标文件类型

* @return 切图map集合

* @throws Exception 异常

*/

public static Map pictureTemplatesCut(File templateFile, File targetFile, String templateType, String targetType) throws Exception {

Map pictureMap = new HashMap<>(2);

if (StringUtils.isEmpty(templateType) || StringUtils.isEmpty(targetType)) {

throw new RuntimeException("file type is empty");

}

InputStream targetIs = new FileInputStream(targetFile);

// 模板图

BufferedImage imageTemplate = ImageIO.read(templateFile);

WIDTH = imageTemplate.getWidth();

HEIGHT = imageTemplate.getHeight();

// 随机生成抠图坐标

generateCutoutCoordinates();

// 最终图像

BufferedImage newImage = new BufferedImage(WIDTH, HEIGHT, imageTemplate.getType());

Graphics2D graphics = newImage.createGraphics();

graphics.setBackground(Color.white);

int bold = 5;

// 获取感兴趣的目标区域

BufferedImage targetImageNoDeal = getTargetArea(X, Y, WIDTH, HEIGHT, targetIs, targetType);

// 根据模板图片抠图

newImage = dealCutPictureByTemplate(targetImageNoDeal, imageTemplate, newImage);

// 设置“抗锯齿”的属性

graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

graphics.setStroke(new BasicStroke(bold, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));

graphics.drawImage(newImage, 0, 0, null);

graphics.dispose();

//新建流。

ByteArrayOutputStream os = new ByteArrayOutputStream();

//利用ImageIO类提供的write方法,将bi以png图片的数据模式写入流。

ImageIO.write(newImage, "png", os);

byte[] newImages = os.toByteArray();

pictureMap.put("newImage", newImages);

// 源图生成遮罩

BufferedImage oriImage = ImageIO.read(targetFile);

byte[] oriCopyImages = dealOriPictureByTemplate(oriImage, imageTemplate, X, Y);

pictureMap.put("oriCopyImage", oriCopyImages);

System.out.println("X="+X+";y="+Y);

return pictureMap;

}

/**

* 抠图后原图生成

* @param oriImage 原始图片

* @param templateImage 模板图片

* @param x 坐标X

* @param y 坐标Y

* @return 添加遮罩层后的原始图片

* @throws Exception 异常

*/

private static byte[] dealOriPictureByTemplate(BufferedImage oriImage, BufferedImage templateImage, int x,

int y) throws Exception {

// 源文件备份图像矩阵 支持alpha通道的rgb图像

BufferedImage oriCopyImage = new BufferedImage(oriImage.getWidth(), oriImage.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);

// 源文件图像矩阵

int[][] oriImageData = getData(oriImage);

// 模板图像矩阵

int[][] templateImageData = getData(templateImage);

//copy 源图做不透明处理

for (int i = 0; i < oriImageData.length; i++) {

for (int j = 0; j < oriImageData[0].length; j++) {

int rgb = oriImage.getRGB(i, j);

int r = (0xff & rgb);

int g = (0xff & (rgb >> 8));

int b = (0xff & (rgb >> 16));

//无透明处理

rgb = r + (g << 8) + (b << 16) + (255 << 24);

oriCopyImage.setRGB(i, j, rgb);

}

}

for (int i = 0; i < templateImageData.length; i++) {

for (int j = 0; j < templateImageData[0].length - 5; j++) {

int rgb = templateImage.getRGB(i, j);

//对源文件备份图像(x+i,y+j)坐标点进行透明处理

if (rgb != 16777215 && rgb <= 0) {

int rgb_ori = oriCopyImage.getRGB(x + i, y + j);

int r = (0xff & rgb_ori);

int g = (0xff & (rgb_ori >> 8));

int b = (0xff & (rgb_ori >> 16));

rgb_ori = r + (g << 8) + (b << 16) + (150 << 24);

oriCopyImage.setRGB(x + i, y + j, rgb_ori);

} else {

//do nothing

}

}

}

//新建流

ByteArrayOutputStream os = new ByteArrayOutputStream();

//利用ImageIO类提供的write方法,将bi以png图片的数据模式写入流

ImageIO.write(oriCopyImage, "png", os);

//从流中获取数据数组

return os.toByteArray();

}

/**

* 根据模板图片抠图

* @param oriImage 原始图片

* @param templateImage 模板图片

* @return 扣了图片之后的原始图片

*/

private static BufferedImage dealCutPictureByTemplate(BufferedImage oriImage, BufferedImage templateImage,

BufferedImage targetImage) throws Exception {

// 源文件图像矩阵

int[][] oriImageData = getData(oriImage);

// 模板图像矩阵

int[][] templateImageData = getData(templateImage);

// 模板图像宽度

for (int i = 0; i < templateImageData.length; i++) {

// 模板图片高度

for (int j = 0; j < templateImageData[0].length; j++) {

// 如果模板图像当前像素点不是白色 copy源文件信息到目标图片中

int rgb = templateImageData[i][j];

if (rgb != 16777215 && rgb <= 0) {

targetImage.setRGB(i, j, oriImageData[i][j]);

}

}

}

return targetImage;

}

/**

* 获取目标区域

* @param x 随机切图坐标x轴位置

* @param y 随机切图坐标y轴位置

* @param targetWidth 切图后目标宽度

* @param targetHeight 切图后目标高度

* @param ois 源文件输入流

* @return 返回目标区域

* @throws Exception 异常

*/

private static BufferedImage getTargetArea(int x, int y, int targetWidth, int targetHeight, InputStream ois,

String fileType) throws Exception {

Iterator imageReaderList = ImageIO.getImageReadersByFormatName(fileType);

ImageReader imageReader = imageReaderList.next();

// 获取图片流

ImageInputStream iis = ImageIO.createImageInputStream(ois);

// 输入源中的图像将只按顺序读取

imageReader.setInput(iis, true);

ImageReadParam param = imageReader.getDefaultReadParam();

Rectangle rec = new Rectangle(x, y, targetWidth, targetHeight);

param.setSourceRegion(rec);

return imageReader.read(0, param);

}

/**

* 生成图像矩阵

* @param bufferedImage 图片流

* @return 图像矩阵

*/

private static int[][] getData(BufferedImage bufferedImage){

int[][] data = new int[bufferedImage.getWidth()][bufferedImage.getHeight()];

for (int i = 0; i < bufferedImage.getWidth(); i++) {

for (int j = 0; j < bufferedImage.getHeight(); j++) {

data[i][j] = bufferedImage.getRGB(i, j);

}

}

return data;

}

/**

* 随机生成抠图坐标

*/

private static void generateCutoutCoordinates() {

Random random = new Random();

// ORI_WIDTH:590 ORI_HEIGHT:360

// WIDTH:93 HEIGHT:360

int widthDifference = ORI_WIDTH - WIDTH;

int heightDifference = ORI_HEIGHT - HEIGHT;

if (widthDifference <= 0) {

X = 5;

} else {

X = random.nextInt(ORI_WIDTH - 3*WIDTH) + 2*WIDTH + 5;

}

if (heightDifference <= 0) {

Y = 5;

} else {

Y = random.nextInt(ORI_HEIGHT - HEIGHT) + 5;

}

NumberFormat numberFormat = NumberFormat.getInstance();

numberFormat.setMaximumFractionDigits(2);

}

}

有了工具类,就可以开始生成图片内容了,我这边直接在Spring的控制器生成内容并返回

@RequestMapping("createImgValidate")

@ResponseBody

public Message createImgValidate(SmsVerificationCodeVo vo){

try {

Integer templateNum = new Random().nextInt(4) + 1;

Integer targetNum = new Random().nextInt(20) + 1;

File templateFile = ResourceUtils.getFile("classpath:static/images/validate/template/"+templateNum+".png");

File targetFile = ResourceUtils.getFile("classpath:static/images/validate/target/"+targetNum+".jpg");

Map pictureMap = VerifyImageUtil.pictureTemplatesCut(templateFile, targetFile,

ConstString.IMAGE_TYPE_PNG,ConstString.IMAGE_TYPE_JPG);

// 将生成的偏移位置信息设置到redis中

String key = ConstString.WEB_VALID_IMAGE_PREFIX + vo.getTelephone();

boolean verified = redisUtil.exists(key);

if(verified){

redisUtil.del(key);

}

redisUtil.set(key,(VerifyImageUtil.getX()+67)+"",SmsUtil.VALID_IMAGE_TIMEOUT);

return ResponseUtil.success(pictureMap);

} catch (Exception e) {

e.printStackTrace();

return ResponseUtil.info(ResponseEnum.BUSINESS_ERROR);

}

}

基本的逻辑是从静态资源中随机加载一张target图片和一张template图片,放到图片处理工具中,处理并返回我们需要的两张图片,生成图片以后,就可以直接返回这个Map了,它会以base64的方式返回到浏览器端。在这里,偏移的位置信息属于敏感内容,它会参与前台传入偏移量的对比校验,所以我这里存到了redis中,返回的内容也就是Map,只不过我用了一个自定义的返回辅助方法(有兴趣的人也可以找我要这些辅助工具)。

二、前端展示图片

首先还是需要在Spring Boot对应的控制器中,加入生成视图的代码(我做图片滑动验证主要为了在发送手机验证码之前做校验,所以有一个手机号的参数)。

/**

* 跳转到图片验证界面

* @return 图片验证界面

*/

@RequestMapping("imgValidate")

public String toImgValidate(ModelMap map, String telephone){

map.addAttribute("telephone",telephone);

return "component/imageValidate";

}

之后便是我们的HTML页码代码:imageValidate.html

图片验证

换一组

拖动滑块完成拼图

var telephone = [[${telephone}]];

然后是对应的JS逻辑代码:imageValidate.js。之前说过后台返回的图片是转成base64了的,所以我们在生成图片的时候,直接在img标签的src内容前加入data:image/png;base64,即可,注意又一个英文逗号。

var left = 0;

$(function(){

// 初始化图片验证码

initImageValidate();

/* 初始化按钮拖动事件 */

// 鼠标点击事件

$("#sliderInner").mousedown(function(){

// 鼠标移动事件

document.onmousemove = function(ev) {

left = ev.clientX;

if(left >= 67 && left <= 563){

$("#sliderInner").css("left",(left-67)+"px");

$("#slideImage").css("left",(left-67)+"px");

}

};

// 鼠标松开事件

document.οnmοuseup=function(){

document.οnmοusemοve=null;

checkImageValidate();

};

});

});

function initImageValidate(){

$.ajax({

async : false,

type : "POST",

url : "/common/createImgValidate",

dataType: "json",

data:{

telephone:telephone

},

success : function(data) {

if(data.status < 400){

// 设置图片的src属性

$("#validateImage").attr("src", "data:image/png;base64,"+data.data.oriCopyImage);

$("#slideImage").attr("src", "data:image/png;base64,"+data.data.newImage);

}else{

layer.open({

icon:2,

title: "温馨提示",

content: data.info

});

}

},

error : function() {}

});

}

function exchange(){

initImageValidate();

}

// 校验

function checkImageValidate(){

$.ajax({

async : false,

type : "POST",

url : "/common/checkImgValidate",

dataType: "json",

data:{

telephone:telephone,

offsetHorizontal:left

},

success : function(data) {

if(data.status < 400){

$("#operateResult").html(data.info).css("color","#28a745");

// 校验通过,调用发送短信的函数

parent.getValidateCode(left);

}else{

$("#operateResult").html(data.info).css("color","#dc3545");

// 验证未通过,将按钮和拼图恢复至原位置

$("#sliderInner").animate({"left":"0px"},200);

$("#slideImage").animate({"left":"0px"},200);

}

},

error : function() {}

});

}

最后是css样式代码:imageValidate.css

body{

overflow: hidden;

}

#container{

width: 100%;

}

.fontDiv{

margin: 16px 0;

}

.dragFont{

font-size: 16px;

color: dodgerblue;

}

.imageDiv{

width: 590px;

height: 360px;

margin: 20px auto 0 auto;

position: relative;

}

.resultDiv{

margin: 10px 20px;

}

#validateImage{

border-radius: 4px;

}

#slideImage{

position: absolute;

top: 5px;

left: 0;

}

#sliderOuter{

width: 590px;

height: 40px;

margin: 12px auto;

border-radius: 20px;

box-shadow: 0 0 10px 5px darkgrey;

display: flex;

align-items: center;

justify-content: center;

position: relative;

}

#dragDiv{

width: 100%;

height: 40px;

position: absolute;

font-size: 16px;

color: dodgerblue;

text-align: center;

line-height: 40px;

-webkit-user-select: none;

-moz-user-select: none;

-ms-user-select: none;

user-select: none;

}

#sliderInner{

width: 94px;

height: 40px;

border-radius: 20px;

font-size: 2rem;

background-color: #28a745;

cursor: pointer;

position: absolute;

left: 0;

}

#sliderInner i{

position: relative;

top: -2px;

left: 36px;

color: white;

}

.coverIcon{

width: 100%;

height: 100%;

position: absolute;

top: 0;

}

资源包下载:Java图片滑动验证

更多关于验证码的文章请点击查看:《java验证码》

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

java 图片滑动验证码_java实现图片滑动验证(包含前端代码)相关推荐

  1. 基于Java实现图片滑动验证(包含前端代码)

    前言 1.下面是一个效果展示: 2.先抱怨一下,在博客上面的抄袭真的非常严重,为了实现一个图片滑动验证,我搜索了挺久的资料,不过内容翻来覆去就是同样的内容,千篇一律,作者还各不相同:内容相同我就不多说 ...

  2. java实现图片验证码_JAVA实现图片验证码

    一.创建image工具类 import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; i ...

  3. java exif 语言编码_Java读取图片EXIF信息的代码

    Java读取图片EXIF信息的代码 本文实例讲述了Java读取图片EXIF信息的方法.分享给大家供大家参考.具体分析如下: 首先介绍一下什么是EXIF,EXIF是Exchangeable Image ...

  4. java图片转字符_java实现图片转字符图(看的过去的亚子)

    普通图片转换为ASSIC码灰度图片 原图: 效果图: 转换方法 读取图片文件到BufferedImage 读取BufferedImage中的RGB值 将RGB三色值按照(0.3,0.59,0.11)权 ...

  5. java 给图片加马赛克_java处理图片--图片的缩放,旋转和马赛克化

    下面是编程之家 jb51.cc 通过网络收集整理的代码片段. 编程之家小编现在分享给大家,也给大家做个参考. 这是我自己结合网上的一些资料封装的java图片处理类,支持图片的缩放,旋转,马赛克化.(转 ...

  6. java 图片上写字_java 在图片上写字,两个图片合并的实现方法

    实例如下: package writeimg; import javax.imageio.ImageIO; import java.awt.Color; import java.awt.Font; i ...

  7. java 两张图片合并_java 在图片上写字,两个图片合并的实现方法

    实例如下: package writeimg; import javax.imageio.ImageIO; import java.awt.Color; import java.awt.Font; i ...

  8. java制作水效果_java实现图片加水印效果

    图片加水印代码,这些代码不常用,但是用到的时候需要注意的地方也挺多的,每次都重写比较麻烦,记下来备忘.代码是图片加水印的一般流程,可根据实际项目需要自行修改. 注:代码在JPG和PNG格式图片下测试通 ...

  9. java获取图片的分辨率_Java读取图片分辨率

    代码依赖于其他工具包,下载请移步:http://download.csdn.net/detail/zhaoguoshuai91/9231957 废话不多说,直接上代码 public class Ima ...

  10. python滑动验证码处理_python+selenium滑动式验证码解决办法

    from selenium importwebdriverfrom selenium.webdriver.support.ui import WebDriverWait #等待元素加载的 from s ...

最新文章

  1. 跟我学Springboot开发后端管理系统2:Mybatis-Plus实战
  2. 深度学习基础——激活函数以及什么时候使用它们?
  3. 强化学习(二)---强化学习介绍
  4. Kaggle入门 (Titanic XGBoost)
  5. 使用 C# 开发浏览器扩展
  6. uniapph5授权成功后返回上一页_记一次授权系统的安全测试
  7. python达梦数据库_python 操作达 梦数据库
  8. Thrift 异步模式
  9. 25 PP模块-创建工厂日历报错-请输入年度xxxx和xxxx之间的有效区域
  10. Linux驱动编写(块设备驱动代码)
  11. .net core下的dotnet全局工具
  12. html邮件的排版问题
  13. Flink中的Window计算-增量计算全量计算
  14. OpenCV-RGB转HSV
  15. 如何使用Node.js来制作电子音乐-编写我们的旋律
  16. 详解FC交换机基础知识
  17. CityMaker学习教程03 数据的导入
  18. Spark基础编程实践
  19. WPS2019参考文献如何引用
  20. JunOS VRRP configuration

热门文章

  1. STM32官方固件库(标准固件库)下载及介绍
  2. 编译原理第三版 作业一
  3. MacOS Ventura 13.0 Beta3 (22A5295i) 带 OC 0.8.2 三分区原版黑苹果镜像
  4. ACM 算法详细分类
  5. Windows内核文件
  6. 【快代理API】设置IP白名单
  7. Ms08-067漏洞抓鸡 方法
  8. Linux多线程编程-线程函数返回值(返回复杂数据类型)
  9. 计算机录入技术五笔输入法教案,五笔打字教案
  10. 写论文visio的使用