使用java自带的awt
DragVerifyCodeUtils 工具类
package com.meeno.common.cerifycode;
import cn.hutool.core.codec.Base64;
import lombok.extern.slf4j.Slf4j;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.Map;
import java.util.Random;
/**
* @Author: wzq
* @Date: Created in 2020/9/23
* @Description: 图片拖动工具类
*/
@Slf4j
public class DragVerifyCodeUtils {
static int targetWidth = 55;//小图长
static int targetHeight = 45;//小图宽
static int circleR = 8;//半径
static int r1 = 4;//距离点
/**
* @Createdate: 2019年1月24日上午10:52:42
* @Title: getBlockData
* @Description: 生成小图轮廓
* @author zhoujin
* @return int[][]
* @throws
*/
private static int[][] getBlockData() {
int[][] data = new int[targetWidth][targetHeight];
double x2 = targetWidth -circleR; //47
//随机生成圆的位置
double h1 = circleR + Math.random() * (targetWidth-3*circleR-r1);
double po = Math.pow(circleR,2); //64
double xbegin = targetWidth - circleR - r1;
double ybegin = targetHeight- circleR - r1;
//圆的标准方程 (x-a)²+(y-b)²=r²,标识圆心(a,b),半径为r的圆
//计算需要的小图轮廓,用二维数组来表示,二维数组有两张值,0和1,其中0表示没有颜色,1有颜色
for (int i = 0; i < targetWidth; i++) {
for (int j = 0; j < targetHeight; j++) {
double d2 = Math.pow(j - 2,2) + Math.pow(i - h1,2);
double d3 = Math.pow(i - x2,2) + Math.pow(j - h1,2);
if ((j <= ybegin && d2 < po)||(i >= xbegin && d3 > po)) {
data[i][j] = 0;
} else {
data[i][j] = 1;
}
}
}
return data;
}
/**
*
* @Createdate: 2019年1月24日上午10:51:30
* @Title: cutByTemplate
* @Description: 有这个轮廓后就可以依据这个二维数组的值来判定抠图并在原图上抠图位置处加阴影,
* @author zhoujin
* @param oriImage 原图
* @param targetImage 抠图拼图
* @param templateImage 颜色
* @param x
* @param y void
* @throws
*/
private static void cutByTemplate(BufferedImage oriImage, BufferedImage targetImage, int[][] templateImage, int x, int y){
int[][] martrix = new int[3][3];
int[] values = new int[9];
//创建shape区域
for (int i = 0; i < targetWidth; i++) {
for (int j = 0; j < targetHeight; j++) {
int rgb = templateImage[i][j];
// 原图中对应位置变色处理
int rgb_ori = oriImage.getRGB(x + i, y + j);
if (rgb == 1) {
targetImage.setRGB(i, j, rgb_ori);
//抠图区域高斯模糊
readPixel(oriImage, x + i, y + j, values);
fillMatrix(martrix, values);
oriImage.setRGB(x + i, y + j, avgMatrix(martrix));
}else{
//这里把背景设为透明
targetImage.setRGB(i, j, rgb_ori & 0x00ffffff);
}
}
}
}
private static void readPixel(BufferedImage img, int x, int y, int[] pixels) {
int xStart = x - 1;
int yStart = y - 1;
int current = 0;
for (int i = xStart; i < 3 + xStart; i++)
for (int j = yStart; j < 3 + yStart; j++) {
int tx = i;
if (tx < 0) {
tx = -tx;
} else if (tx >= img.getWidth()) {
tx = x;
}
int ty = j;
if (ty < 0) {
ty = -ty;
} else if (ty >= img.getHeight()) {
ty = y;
}
pixels[current++] = img.getRGB(tx, ty);
}
}
private static void fillMatrix(int[][] matrix, int[] values) {
int filled = 0;
for (int i = 0; i < matrix.length; i++) {
int[] x = matrix[i];
for (int j = 0; j < x.length; j++) {
x[j] = values[filled++];
}
}
}
private static int avgMatrix(int[][] matrix) {
int r = 0;
int g = 0;
int b = 0;
for (int i = 0; i < matrix.length; i++) {
int[] x = matrix[i];
for (int j = 0; j < x.length; j++) {
if (j == 1) {
continue;
}
Color c = new Color(x[j]);
r += c.getRed();
g += c.getGreen();
b += c.getBlue();
}
}
return new Color(r / 8, g / 8, b / 8).getRGB();
}
/**
* @return Map<String, Object> 返回生成的抠图和带抠图阴影的大图 base64码及抠图坐标
* @Description: 读取网络图片,生成拼图验证码
* @author zhoujin
*/
public static Map<String, Object> createImage(String imgUrl, Map<String, Object> resultMap) {
try {
//通过URL 读取图片
URL url = new URL(imgUrl);
BufferedImage bufferedImage = ImageIO.read(url.openStream());
Random rand = new Random();
int widthRandom = rand.nextInt(bufferedImage.getWidth() - targetWidth - 100 + 1) + 100;
int heightRandom = rand.nextInt(bufferedImage.getHeight() - targetHeight + 1);
log.info("原图大小{} x {},随机生成的坐标 X,Y 为({},{})", bufferedImage.getWidth(), bufferedImage.getHeight(), widthRandom, heightRandom);
BufferedImage target = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_4BYTE_ABGR);
//输出裁剪后的图片
ImageIO.write(target, "jpg", new FileOutputStream("D:\\work\\temp\\cuttingImage.jpg"));
cutByTemplate(bufferedImage, target, getBlockData(), widthRandom, heightRandom);
resultMap.put("bigImage", getImageBASE64(bufferedImage));//大图
resultMap.put("smallImage", getImageBASE64(target));//小图
resultMap.put("xWidth", widthRandom);
resultMap.put("yHeight", heightRandom);
log.info("-----");
log.info(getImageBASE64(target));
//输出原图
ImageIO.write(bufferedImage, "jpg", new FileOutputStream("D:\\work\\temp\\img.jpg"));
} catch (Exception e) {
log.info("创建图形验证码异常", e);
} finally {
return resultMap;
}
}
private static String getImageBASE64(BufferedImage image) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ImageIO.write(image, "png", out);
byte[] bytes = out.toByteArray();
String str = Base64.encode(bytes);
str = "data:image/jpg;base64," + str;
return str;
}
}
获取动态拖动码
/**
* 获取动态拖动码
* @return
*/
@RequestMapping("getDragVerifyCode.action")
public ResponseBean getDragVerifyCode() {
//图库地址 定制 宽 300 高 200的图片
String path = "https://picsum.photos/300/200";
Map<String, Object> resultMap = new HashMap<>();
DragVerifyCodeUtils.createImage(path, resultMap);
//save redis
String uuid = IdUtil.simpleUUID();
String key = "RandomVerify:" + uuid;
RedisUtil.set(key, resultMap, 180);
resultMap.put("uuid", uuid);
return ResultUtil.success(resultMap);
}
校验
/**
* 账号登录
* @param session
* @param phone
* @param pwd
* @param entryType
* @return
*/
@RequestMapping("accountLogin.do")
public ResponseBean accountLogin(final HttpSession session, String phone, String pwd, String entryType,
String uuid, Double targetX){
//校验验证码
MeenoAssert.notNull(targetX,"targetX can not empty!");
MeenoAssert.hasLength(uuid, "uuid can not empty!");
Object randomVerifyObj = RedisUtil.get("RandomVerify:" + uuid);
//删除验证码
RedisUtil.del("RandomVerify:" + uuid);
MeenoAssert.notNull(randomVerifyObj, CErrEnum.RANDOM_VERIFY_CODE_FAILURE);
Map<String, Object> randomVerifyCodeMap = (Map<String, Object>) randomVerifyObj;
Object xWidth = randomVerifyCodeMap.get("xWidth");
double x = Double.parseDouble(xWidth.toString());
MeenoAssert.notTrue(Math.abs(x - targetX) > 10,CErrEnum.RANDOM_VERIFY_ERR);
/*MeenoAssert.notNull(randomCerifyCodeObj, CErrEnum.RANDOM_VERIFY_CODE_FAILURE);
MeenoAssert.isTrue(randomCerifyCode.toLowerCase().equals(randomCerifyCodeObj.toString().toLowerCase()), CErrEnum.RANDOM_VERIFY_CODE_ERR);*/
LoginResult loginResult = this.employeeService.accountLogin(session, phone, pwd, entryType);
EmpView employeeView = this.employeeService.getEmployee(loginResult.getUserInfo().getId());
Map<String,Object> resultMap = Maps.newHashMap();
resultMap.put("loginResult", loginResult);
resultMap.put("employee", employeeView);
return ResultUtil.success(resultMap);
}