大家好,欢迎来到IT知识分享网。
在大多数系统中都会有一个防止机器爆破使用某个功能接口,比如有登录、发送短信、下载导出等等,这些功能通常比较敏感的并且发生频率不允许太高,而为了防止机器循环爆破或者用户误触,所以需要在发生功能事件做一个防机器拦截,而如今大多数是使用拖动滑块图片验证来实现其目地的;拖动滑块图片验证中滑块每次所需正确位置完全随机,背景图片也将随机刷新,每次滑错都将重置滑块正确位置和背景图片,使得机器无法自动识别并且暴力激活成功教程,至此实现了其功能。
本文章将用Java语言实现拖动滑块图片验证

1、静态文件
首先准备好静态文件放在项目的resource静态目录,需要的背景图、滑块交互js、样式css、以及ui组件的图片
2、渲染滑块界面接口
该接口需要返回4个参数纵轴坐标Y(本功能只修改横坐标)、背景图imgBg、背景抠图imgBtn、以及标识tokenId;
/ * 验证码生成 * @return * @throws IOException */ Result verifyCodeInit() throws IOException;
业务具体实现:
@Override public Result verifyCodeInit() throws IOException { Map<String,Object> result = new HashMap<>(); /*本地缓存实现*/ List<byte[]> imgList = null; // VerifyEntity verify = redisTemplate.get( KEY_VALIDATE_IMG,VerifyEntity.class); // if (!ObjectUtil.ObjectIsNull(verify)) { // imgList = verify.getImgList(); // } // // if (CollectionUtils.isEmpty(imgList)) { // imgList = initFileByte(); // VerifyEntity verifyEntity = new VerifyEntity(); // verifyEntity.setImgList(imgList); // redisTemplate.set(KEY_VALIDATE_IMG, verifyEntity); // } //加载内存中的图片 imgList = DEFAULT_IMG_LIST; //随机取出一张验证图 Random ra = new Random(); int rd = ra.nextInt(imgList.size() - 1); byte[] targetIs = imgList.get(rd); //生成验证码 PuzzleCaptcha puzzleCaptcha = new PuzzleCaptcha(targetIs); puzzleCaptcha.setImageQuality(Image.SCALE_AREA_AVERAGING); puzzleCaptcha.run(); //抠块图 String imgBtn = ImageConvertUtil.toDataUri(puzzleCaptcha.getVacancy(), "png"); //背景图 String imgBg = ImageConvertUtil.toDataUri(puzzleCaptcha.getArtwork(), "png"); String token = UUID.randomUUID().toString().replaceAll("-", ""); Map<String, Object> cacheObj = new HashMap<>(5); cacheObj.put("token", token); //偏移量 cacheObj.put("X", puzzleCaptcha.getX()); cacheObj.put("Y", puzzleCaptcha.getY()); //验证起始时间 cacheObj.put("time", System.currentTimeMillis()); //保存验证状态 cacheObj.put("verifyCount", 0); //保存2分钟 redisKit.set(KEY_VALIDATE_TOKEN+":"+token, cacheObj, 120); result.put("imgBtn", imgBtn); result.put("imgBg", imgBg); result.put("tokenId", token); result.put("Y", puzzleCaptcha.getY()); return Result.me().success().setData(result); }
初始化背景图片的方法:
private static List<byte[]> DEFAULT_IMG_LIST; static { try { DEFAULT_IMG_LIST = initFileByte(); //未加载到图片或缓存被外部清除 if (DEFAULT_IMG_LIST == null || DEFAULT_IMG_LIST.size() < 1) { //重新加载 DEFAULT_IMG_LIST = initFileByteNew(); } } catch (IOException e) { System.out.println("加载背景图失败"); } } / * 读取文件字节流 */ private static List<byte[]> initFileByte() throws IOException { //图片存储路径在resource/static/verify_imgs包下 String imgPath = "META-INF/static/app/images/verify_imgs"; URL url = Thread.currentThread().getContextClassLoader().getResource(imgPath); if (url != null) { String protocol = url.getProtocol(); System.out.println("protocol ======> " + protocol); return "jar".equalsIgnoreCase(protocol) ? initJarImg(url) : initClassImg(url); } return null; } / * 加载项目资源图片文件 * * @param url * @return */ private static List<byte[]> initClassImg(URL url) throws IOException { if (url != null) { File fileDir = new File(url.getPath()); File[] fs = fileDir.listFiles(); if (fs != null && fs.length > 0) { List<byte[]> byteList = new ArrayList<>(); byte[] bytes; for (File f : fs) { bytes = IOUtils.toByteArray(new FileInputStream(f)); byteList.add(bytes); } return byteList; } } return null; } / * 加载jar包资源图片文件 * * @param url * @return */ private static List<byte[]> initJarImg(URL url) throws IOException { DSFLog.logDebug(url); if (url != null) { DSFLog.info("再执行读取jar包文件方法 url="+url); String jarPath = url.getPath().substring(0, url.getPath().indexOf("!/") + 2); URL jarUrl = new URL("jar:" + jarPath); JarURLConnection jarCon = (JarURLConnection) jarUrl.openConnection(); if (jarCon != null) { DSFLog.info("JarURLConnection 成功"+jarCon); JarFile jarFile = jarCon.getJarFile(); Enumeration<JarEntry> jarEntrys = jarFile.entries(); byte[] bytes; List<byte[]> byteList = new ArrayList<>(); while (jarEntrys.hasMoreElements()) { JarEntry entry = jarEntrys.nextElement(); String name = entry.getName(); //打包jar后的图片资源地址 if (name.startsWith("META-INF/classes/static/verify_imgs") && name.contains(".jpg")) { bytes = IOUtils.toByteArray(ImgValidationController.class.getClassLoader().getResourceAsStream(name)); byteList.add(bytes); } } return byteList; }else{ DSFLog.info("JarURLConnection 失败"+jarCon); } } return null; } / * 读取文件字节流 */ private static List<byte[]> initFileByteNew() throws IOException { //图片存储路径在resource/static/verify_imgs包下 List<byte[]> allImg = new ArrayList<>();; for (int i = 1; i <= 10; i++) { try { String imgUrl = VerifyServiceImpl.class.getResource("/").getPath().concat("templates/verify_imgs/").concat(String.valueOf(i)).concat(".jpg"); InputStream inputStream = new FileInputStream(imgUrl); byte[] imgByte = convert2ByteArray(inputStream) ; allImg.add(imgByte); }catch (Exception e){ String imgUrl = "templates/verify_imgs/" + i + ".jpg"; InputStream inputStream = VerifyServiceImpl.class.getClassLoader().getResourceAsStream(imgUrl); byte[] imgByte = convert2ByteArray(inputStream) ; allImg.add(imgByte); } } return allImg; }
PuzzleCaptcha实体类
public class PuzzleCaptcha { / 默认宽度,用来计算阴影基本长度 */ private static final int DEFAULT_WIDTH = 350; / 随机数 */ private static final Random RANDOM = new Random(); / 蒙版 */ private static Color color = new Color(108, 104, 220, 204); / alpha通道过滤器 */ private static InvertAlphaFilter alphaFilter = new InvertAlphaFilter(); / 边距 */ private static int margin = 50; / 生成图片的宽度 */ private int width = DEFAULT_WIDTH; / 生成图片高度 */ private int height = 160; / 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 = true; / 小图描边颜色 */ private Color vacancyBorderColor = new Color(250, 252, 200, 255); / 小图描边线条的宽度 */ private float vacancyBorderWidth = 3f; / 主图描边的颜色 */ 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_DEFAULT; / * 从文件中读取图片 * * @param file */ // public PuzzleCaptcha(File file) { // try { // image = ImageIO.read(new ByteArrayInputStream(bytes)); // } catch (IOException var2) { // throw new IORuntimeException(var2); // } // } / * 从文件中读取图片,请使用绝对路径,使用相对路径会相对于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) { try { this.image = ImageIO.read(new ByteArrayInputStream(bytes)); } catch (IOException var2) { throw new IORuntimeException(var2); } try { this.image = ImageIO.read(new ByteArrayInputStream(bytes)); } catch (IOException var2) { throw new IORuntimeException(var2); } } / * 生成随机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); wScale = NumberUtil.div(w, width); path = paintBrick(wScale, hScale); } // 创建阴影过滤器 float radius = 5 * ((float) w / DEFAULT_WIDTH) * (float) wScale; int left = 1; ShadowFilter shadowFilter = new ShadowFilter(radius, 2 * (float) wScale, -1 * (float) hScale, 0.8f); // 创建空白的图片 BufferedImage artwork = translucent(new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB)); BufferedImage localVacancy = translucent(new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB)); // 画小图 Graphics2D vg = localVacancy.createGraphics(); // 抗锯齿 vg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // 设置画图路径范围 vg.setClip(path); // 将区域中的图像画到小图中 vg.drawImage(thumbnail, null, null); //描边 if (vacancyBorderColor != null) { vg.setColor(vacancyBorderColor); vg.setStroke(new BasicStroke(vacancyBorderWidth)); vg.draw(path); } // 释放图像 vg.dispose(); // 画大图 // 创建画笔 Graphics2D g = artwork.createGraphics(); // 抗锯齿 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // 画上图片 g.drawImage(thumbnail, null, null); // 设置画图路径范围 g.setClip(path); // 填充缺口透明度 颜色混合,不透明在上 g.setComposite(AlphaComposite.SrcAtop); // 填充一层白色的透明蒙版,透明度越高,白色越深 alpha:0-255 g.setColor(color); g.fill(path); //描边 if (artworkBorderColor != null) { g.setColor(artworkBorderColor); g.setStroke(new BasicStroke(artworkBorderWidth)); g.draw(path); } // 画上基于小图的内阴影,先反转alpha通道,然后创建阴影 g.drawImage(shadowFilter.filter(alphaFilter.filter(localVacancy, null), null), null, null); // 释放图像 g.dispose(); // 裁剪掉多余的透明背景 localVacancy = ImageUtils.getSubimage(localVacancy, (int) (x * wScale - left), 0, (int) Math.ceil(path.getBounds().getWidth() + radius) + left, h); if (isFast) { // 添加阴影 this.vacancy = shadowFilter.filter(localVacancy, null); this.artwork = artwork; } else { // 小图添加阴影 localVacancy = shadowFilter.filter(localVacancy, null); // 大图缩放 this.artwork = artwork.getScaledInstance(width, height, imageQuality); // 缩放时,需要加上阴影的宽度,再除以放大比例 this.vacancy = localVacancy.getScaledInstance((int) ((path.getBounds().getWidth() + radius) / wScale), height, imageQuality); } } / * 绘制拼图块的路径 * * @param xScale x轴放大比例 * @param yScale y轴放大比例 * @return */ private GeneralPath paintBrick(double xScale, double yScale) { double x = this.x * xScale; double y = this.y * yScale; // 直线移动的基础距离 double hMoveL = vwh / 3f * yScale; double wMoveL = vwh / 3f * xScale; GeneralPath path = new GeneralPath(); path.moveTo(x, y); path.lineTo(x + wMoveL, y); // 上面的圆弧正东方向0°,顺时针负数,逆时针正数 path.append(arc(x + wMoveL, y - hMoveL / 2, wMoveL, hMoveL, 180, -180), true); path.lineTo(x + wMoveL * 3, y); path.lineTo(x + wMoveL * 3, y + hMoveL); // 右边的圆弧 path.append(arc(x + wMoveL * 2 + wMoveL / 2, y + hMoveL, wMoveL, hMoveL, 90, -180), true); path.lineTo(x + wMoveL * 3, y + hMoveL * 3); path.lineTo(x, y + hMoveL * 3); path.lineTo(x, y + hMoveL * 2); // 左边的内圆弧 path.append(arc(x - wMoveL / 2, y + hMoveL, wMoveL, hMoveL, -90, 180), true); path.lineTo(x, y); path.closePath(); return path; } / * 绘制圆形、圆弧或者是椭圆形 * 正东方向0°,顺时针负数,逆时针正数 * * @param x 左上角的x坐标 * @param y 左上角的y坐标 * @param w 宽 * @param h 高 * @param start 开始的角度 * @param extent 结束的角度 * @return */ private Arc2D arc(double x, double y, double w, double h, double start, double extent) { return new Arc2D.Double(x, y, w, h, start, extent, Arc2D.OPEN); } / * 透明背景 * * @param bufferedImage * @return */ private BufferedImage translucent(BufferedImage bufferedImage) { Graphics2D g = bufferedImage.createGraphics(); bufferedImage = g.getDeviceConfiguration().createCompatibleImage(bufferedImage.getWidth(), bufferedImage.getHeight(), Transparency.TRANSLUCENT); g.dispose(); return bufferedImage; } / * 随机数 * * @param min * @param max * @return */ private static int random(int min, int max) { return RANDOM.nextInt((max - min) + 1) + min; } public static Color getColor() { return color; } public static void setColor(Color color) { PuzzleCaptcha.color = color; } public static InvertAlphaFilter getAlphaFilter() { return alphaFilter; } public static void setAlphaFilter(InvertAlphaFilter alphaFilter) { PuzzleCaptcha.alphaFilter = alphaFilter; } public static int getMargin() { return margin; } public static void setMargin(int margin) { PuzzleCaptcha.margin = margin; } public int getWidth() { return width; } public void setWidth(int width) { this.width = width; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } public int getVwh() { return vwh; } public void setVwh(int vwh) { this.vwh = vwh; } public Image getImage() { return image; } public void setImage(Image image) { this.image = image; } public Image getArtwork() { return artwork; } public void setArtwork(Image artwork) { this.artwork = artwork; } public Image getVacancy() { return vacancy; } public void setVacancy(Image vacancy) { this.vacancy = vacancy; } public boolean isFast() { return isFast; } public void setFast(boolean fast) { isFast = fast; } public Color getVacancyBorderColor() { return vacancyBorderColor; } public void setVacancyBorderColor(Color vacancyBorderColor) { this.vacancyBorderColor = vacancyBorderColor; } public float getVacancyBorderWidth() { return vacancyBorderWidth; } public void setVacancyBorderWidth(float vacancyBorderWidth) { this.vacancyBorderWidth = vacancyBorderWidth; } public Color getArtworkBorderColor() { return artworkBorderColor; } public void setArtworkBorderColor(Color artworkBorderColor) { this.artworkBorderColor = artworkBorderColor; } public float getArtworkBorderWidth() { return artworkBorderWidth; } public void setArtworkBorderWidth(float artworkBorderWidth) { this.artworkBorderWidth = artworkBorderWidth; } public double getMaxRatio() { return maxRatio; } public void setMaxRatio(double maxRatio) { this.maxRatio = maxRatio; } public int getImageQuality() { return imageQuality; } public void setImageQuality(int imageQuality) { this.imageQuality = imageQuality; } }
ImageConvertUtil工具类
public class ImageConvertUtil { / * 将image对象转为base64字符串 * * @param image * @return */ public static String toBase64(Image image, String format) { BASE64Encoder encoder = new BASE64Encoder(); return encoder.encode(toBytes(image, format)); } / * 将image对象转为前端img标签识别的base64字符串 * * @param image * @param format * @return */ public static String toDataUri(Image image, String format) { return String.format("data:image/%s;base64,%s", format, toBase64(image, format)); } / * 将image对象转为字节 * * @param image * @param format * @return */ public static byte[] toBytes(Image image, String format) { ByteArrayOutputStream stream = new ByteArrayOutputStream(); try { ImageIO.write(ImageUtils.convertImageToARGB(image), format, stream); } catch (IOException e) { e.printStackTrace(); } return stream.toByteArray(); } }
3、前端页面上加上调用
html文件
<button type="button" class="getYzm" onclick="showPopup()">获取验证码</button>
js文件
//新版获取滑动验证码 start // 图片验证 var dataList = ["0","1"]; var codeObj={}; var options = { dataList: dataList, success:function(obj){ //动态验证码验证通过回调 hidePopup(); codeObj = obj; sendCode(); }, fail: function(){ } }; var SliderBarInfo; // 弹窗 var overlay = document.getElementById("overlay"); function showPopup(){ if (!$("#loginName").val()) { dsf.layer.message("请输入用户名!",false); return; } // if (!$("#password").val()) { // dsf.layer.message("请输入密码!",false); // return; // } SliderBarInfo = SliderBar("slideBar", options); overlay.classList.add('show') } function hidePopup(){ overlay.classList.remove('show') } //新版获取滑动验证码 end
此处用到的SliderBar来自slider.js,已上传至绑定资源
滑块验证通过后 发送登录接口再次验证(可以不再验证)
//获取验证码 function sendCode(piccode){ // $(".hxphone").hide(); var phone = $("#loginName").val().trim(); var flag = ''; if($(".right .tab li.active").attr("data-id") == 7){ flag = "stu"; }else if ($(".right .tab li.active").attr("data-id") == 8){ flag = "tea"; }else { flag = "dw"; } if(!codeDisabled){ var params = {phone:phone,tokenId:codeObj.tokenId,x:codeObj.x,y:codeObj.y,flag:flag}; codeDisabled = true; $(".getYzm").attr("disabled","disabled"); dsf.http.request(dsf.url.getWebPath("com/teas/sendsms/sendcode"),params, "GET") .done(function (res) { if (res.success) { top.layer.closeAll(); yzmid = res.data; dsf.layer.message("验证码已发送,请注意查收",true); $(".getYzm").text(second+"s后重新获取"); getSjhm(params); codetimer=setTimeout(function(){ startTime(); },1000); } else { dsf.layer.message(res.message); codeDisabled = false; $(".getYzm").removeAttr("disabled"); loadrcode(); } }) .error(function (err) { dsf.layer.message("发送失败,请联系管理员!"); codeDisabled = false; $(".getYzm").removeAttr("disabled"); }) .always(function () { }) .exec(); } }
4、检查滑块位置是否正确
/ * 验证码验证 * @param tokenId 验证码id * @param x * @param y * @return */ Result checkVerifyCode(String tokenId, Integer x, Integer y);
@Override public Result checkVerifyCode(String tokenId, Integer x, Integer y) { JSONObject message = new JSONObject(); int code; String resultStr; double time = 0.00; if (StringUtils.isEmpty(tokenId) || x == null || y == null) { message.put("code", 0); // message.put("message", "请求参数错误:参数不能为空"); message.put("message", "验证不通过,请重试!"); return Result.me().error().setData(message).setMessage("验证不通过,请重试!"); } Map<String, Object> cacheObj = (Map<String, Object>) redisKit.get(KEY_VALIDATE_TOKEN+":"+tokenId); if (null == cacheObj) { code = -1; resultStr = "验证码超期,请重新请求!"; } else { int sX = (Integer) cacheObj.get("X"); int sY = (Integer) cacheObj.get("Y"); int sStatus = (Integer) cacheObj.get("verifyCount"); if (sY != y) { code = 0; // resultStr = "请求参数错误:位置信息不正确!"; resultStr = "验证不通过,请重试!"; } else { if (Math.abs(sX - x) <= CHECK_SCOPE) { code = 1; resultStr = "验证通过!"; time = System.currentTimeMillis() - (Long) cacheObj.get("time"); //更新验证状态,存储60s cacheObj.put("verifyCount", sStatus + 1); // redisTemplate.set(KEY_VALIDATE_TOKEN+":"+tokenId, cacheObj, 60); //存储新token String token = UUID.randomUUID().toString().replaceAll("-", ""); cacheObj.put("token",token); redisKit.set(KEY_VALIDATE_TOKEN+":"+token, cacheObj, 120); message.put("tokenId", token); } else { code = 2; resultStr = "验证不通过,请重试!"; } } //删除旧token redisKit.del(KEY_VALIDATE_TOKEN+":"+tokenId); } message.put("time", NumberUtil.div(time * 1.00, 1000.00, 2)); message.put("code", code); message.put("message", resultStr); if (code == 1){ return Result.me().success().setData(message); }else{ return Result.me().error().setData(message).setMessage(resultStr); } }
5、业务层全部代码
/ * <p> * 验证码信息 服务实现类 * </p> */ @Service public class VerifyServiceImpl implements VerifyService { @Autowired private RedisTemplate redisTemplate; @Autowired RedisKit redisKit; / * 验证码缓存KEY */ private static final String KEY_VERIFY_CODE = "KEY_VERIFY_CODE"; / * Token Cache Key */ private static final String KEY_VALIDATE_TOKEN = "KEY_VALIDATE_TOKEN"; / * Img Cache Key */ private static final String KEY_VALIDATE_IMG = "KEY_VALIDATE_IMG"; / * 校验误差范围 px */ private static final Integer CHECK_SCOPE = 10; private static List<byte[]> DEFAULT_IMG_LIST; static { try { DEFAULT_IMG_LIST = initFileByte(); //未加载到图片或缓存被外部清除 if (DEFAULT_IMG_LIST == null || DEFAULT_IMG_LIST.size() < 1) { //重新加载 DEFAULT_IMG_LIST = initFileByteNew(); } } catch (IOException e) { System.out.println("加载背景图失败"); } } @Override public Result verifyCodeInit() throws IOException { Map<String,Object> result = new HashMap<>(); /*本地缓存实现*/ List<byte[]> imgList = null; // VerifyEntity verify = redisTemplate.get( KEY_VALIDATE_IMG,VerifyEntity.class); // if (!ObjectUtil.ObjectIsNull(verify)) { // imgList = verify.getImgList(); // } // // if (CollectionUtils.isEmpty(imgList)) { // imgList = initFileByte(); // VerifyEntity verifyEntity = new VerifyEntity(); // verifyEntity.setImgList(imgList); // redisTemplate.set(KEY_VALIDATE_IMG, verifyEntity); // } //加载内存中的图片 imgList = DEFAULT_IMG_LIST; //随机取出一张验证图 Random ra = new Random(); int rd = ra.nextInt(imgList.size() - 1); byte[] targetIs = imgList.get(rd); //生成验证码 PuzzleCaptcha puzzleCaptcha = new PuzzleCaptcha(targetIs); puzzleCaptcha.setImageQuality(Image.SCALE_AREA_AVERAGING); puzzleCaptcha.run(); //抠块图 String imgBtn = ImageConvertUtil.toDataUri(puzzleCaptcha.getVacancy(), "png"); //背景图 String imgBg = ImageConvertUtil.toDataUri(puzzleCaptcha.getArtwork(), "png"); String token = UUID.randomUUID().toString().replaceAll("-", ""); Map<String, Object> cacheObj = new HashMap<>(5); cacheObj.put("token", token); //偏移量 cacheObj.put("X", puzzleCaptcha.getX()); cacheObj.put("Y", puzzleCaptcha.getY()); //验证起始时间 cacheObj.put("time", System.currentTimeMillis()); //保存验证状态 cacheObj.put("verifyCount", 0); //保存2分钟 redisKit.set(KEY_VALIDATE_TOKEN+":"+token, cacheObj, 120); result.put("imgBtn", imgBtn); result.put("imgBg", imgBg); result.put("tokenId", token); result.put("Y", puzzleCaptcha.getY()); return Result.me().success().setData(result); } @Override public Result checkVerifyCode(String tokenId, Integer x, Integer y) { JSONObject message = new JSONObject(); int code; String resultStr; double time = 0.00; if (StringUtils.isEmpty(tokenId) || x == null || y == null) { message.put("code", 0); // message.put("message", "请求参数错误:参数不能为空"); message.put("message", "验证不通过,请重试!"); return Result.me().error().setData(message).setMessage("验证不通过,请重试!"); } Map<String, Object> cacheObj = (Map<String, Object>) redisKit.get(KEY_VALIDATE_TOKEN+":"+tokenId); if (null == cacheObj) { code = -1; resultStr = "验证码超期,请重新请求!"; } else { int sX = (Integer) cacheObj.get("X"); int sY = (Integer) cacheObj.get("Y"); int sStatus = (Integer) cacheObj.get("verifyCount"); if (sY != y) { code = 0; // resultStr = "请求参数错误:位置信息不正确!"; resultStr = "验证不通过,请重试!"; } else { if (Math.abs(sX - x) <= CHECK_SCOPE) { code = 1; resultStr = "验证通过!"; time = System.currentTimeMillis() - (Long) cacheObj.get("time"); //更新验证状态,存储60s cacheObj.put("verifyCount", sStatus + 1); // redisTemplate.set(KEY_VALIDATE_TOKEN+":"+tokenId, cacheObj, 60); //存储新token String token = UUID.randomUUID().toString().replaceAll("-", ""); cacheObj.put("token",token); redisKit.set(KEY_VALIDATE_TOKEN+":"+token, cacheObj, 120); message.put("tokenId", token); } else { code = 2; resultStr = "验证不通过,请重试!"; } } //删除旧token redisKit.del(KEY_VALIDATE_TOKEN+":"+tokenId); } message.put("time", NumberUtil.div(time * 1.00, 1000.00, 2)); message.put("code", code); message.put("message", resultStr); if (code == 1){ return Result.me().success().setData(message); }else{ return Result.me().error().setData(message).setMessage(resultStr); } } / * 读取文件字节流 */ private static List<byte[]> initFileByte() throws IOException { //图片存储路径在resource/static/verify_imgs包下 String imgPath = "META-INF/static/app/images/verify_imgs"; URL url = Thread.currentThread().getContextClassLoader().getResource(imgPath); if (url != null) { String protocol = url.getProtocol(); System.out.println("protocol ======> " + protocol); return "jar".equalsIgnoreCase(protocol) ? initJarImg(url) : initClassImg(url); } return null; } / * 读取文件字节流 */ private static List<byte[]> initFileByteNew() throws IOException { //图片存储路径在resource/static/verify_imgs包下 DSFLog.info("在执行自己写的获取图片方法"); List<byte[]> allImg = new ArrayList<>();; for (int i = 1; i <= 10; i++) { try { String imgUrl = VerifyServiceImpl.class.getResource("/").getPath().concat("templates/verify_imgs/").concat(String.valueOf(i)).concat(".jpg"); InputStream inputStream = new FileInputStream(imgUrl); byte[] imgByte = convert2ByteArray(inputStream) ; allImg.add(imgByte); }catch (Exception e){ String imgUrl = "templates/verify_imgs/" + i + ".jpg"; InputStream inputStream = VerifyServiceImpl.class.getClassLoader().getResourceAsStream(imgUrl); byte[] imgByte = convert2ByteArray(inputStream) ; allImg.add(imgByte); } } DSFLog.info("执行自己写的获取图片方法完成,图片数量:"+allImg.size()); return allImg; } / * inputStream转为byte[] * * @param inStream * @return byte[] * @throws IOException */ public static byte[] convert2ByteArray(InputStream inStream) throws IOException { ByteArrayOutputStream swapStream = new ByteArrayOutputStream(); byte[] buff = new byte[1024]; int rc = 0; while ((rc = inStream.read(buff, 0, 1024)) > 0) { swapStream.write(buff, 0, rc); } byte[] in_b = swapStream.toByteArray();// in_b为转换之后的结果 return in_b; } / * 加载项目资源图片文件 * * @param url * @return */ private static List<byte[]> initClassImg(URL url) throws IOException { if (url != null) { File fileDir = new File(url.getPath()); File[] fs = fileDir.listFiles(); if (fs != null && fs.length > 0) { List<byte[]> byteList = new ArrayList<>(); byte[] bytes; for (File f : fs) { bytes = IOUtils.toByteArray(new FileInputStream(f)); byteList.add(bytes); } return byteList; } } return null; } / * 加载jar包资源图片文件 * * @param url * @return */ private static List<byte[]> initJarImg(URL url) throws IOException { DSFLog.logDebug(url); if (url != null) { DSFLog.info("再执行读取jar包文件方法 url="+url); String jarPath = url.getPath().substring(0, url.getPath().indexOf("!/") + 2); URL jarUrl = new URL("jar:" + jarPath); JarURLConnection jarCon = (JarURLConnection) jarUrl.openConnection(); if (jarCon != null) { DSFLog.info("JarURLConnection 成功"+jarCon); JarFile jarFile = jarCon.getJarFile(); Enumeration<JarEntry> jarEntrys = jarFile.entries(); byte[] bytes; List<byte[]> byteList = new ArrayList<>(); while (jarEntrys.hasMoreElements()) { JarEntry entry = jarEntrys.nextElement(); String name = entry.getName(); //打包jar后的图片资源地址 if (name.startsWith("META-INF/classes/static/verify_imgs") && name.contains(".jpg")) { bytes = IOUtils.toByteArray(ImgValidationController.class.getClassLoader().getResourceAsStream(name)); byteList.add(bytes); } } return byteList; }else{ DSFLog.info("JarURLConnection 失败"+jarCon); } } return null; } }
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/110988.html

