在路上

 找回密码
 立即注册
在路上 站点首页 学习 查看内容

Java仿12306图片验证码

2016-7-29 15:41| 发布者: zhangjf| 查看: 502| 评论: 0

摘要: 由于要做一个新项目,所以打算做一个简单的图片验证码。 先说说思路吧:在服务端,从一个文件夹里面找出8张图片,再把8张图片合并成一张大图,在8个小图里面随机生成一个要用户验证的图片分类,如小狗、啤酒等。在前 ...

由于要做一个新项目,所以打算做一个简单的图片验证码。

先说说思路吧:在服务端,从一个文件夹里面找出8张图片,再把8张图片合并成一张大图,在8个小图里面随机生成一个要用户验证的图片分类,如小狗、啤酒等。在前端,访问这个页面时,把图片加载上去,用户在图片上选择提示所需要的图片,当用户点登陆时,根据用户选择的所有坐标判断所选的图片是不是实际上的验证图片。

先放两张效果图:

为了让文件查找比较简单,在图片文件结构上可以这样:

这样方便生成用户要选择的Key图片,和取出8张小图合并成大图。

上代码:这是选择8张图片,并且在每张图片选取时用递归保证选取的图片不会重复。

  1. //选取8个图片
  2. public static List<Object> getEightImages() {
  3. //保存取到的每一个图片的path,保证图片不会重复
  4. List<String> paths = new ArrayList<String>();
  5. File[] finalImages = new File[8];
  6. List<Object> object = new ArrayList<Object>();
  7. //保存tips
  8. String[] tips = new String[8];
  9. for (int i = 0; i < 8; i++) {
  10. //获取随机的二级目录
  11. int dirIndex = getRandom(secondaryDirNumbers);
  12. File secondaryDir = getFiles()[dirIndex];
  13. //随机到的文件夹名称保存到tips中
  14. tips[i] = secondaryDir.getName();
  15. //获取二级图片目录下的文件
  16. File[] images = secondaryDir.listFiles();
  17. int imageIndex = getRandom(imageRandomIndex);
  18. File image = images[imageIndex];
  19. //图片去重
  20. image = dropSameImage(image, paths, tips, i);
  21. paths.add(image.getPath());
  22. finalImages[i] = image;
  23. }
  24. object.add(finalImages);
  25. object.add(tips);
  26. return object;
  27. }
复制代码

在生成这8张图片中,用一个数组保存所有的文件分类。在这个分类里面可以用随机数选取一个分类做为Key分类,就是用户要选择的所有图片。由于数组是有序的,可以遍历数组中的元素,获取每个key分类图片的位置,方便在用户验证时,进行匹配。

  1. //获取位置,返回的是第几个图片,而不是下标,从1开始,集合第一个元素为tip
  2. public static List<Object> getLocation(String[] tips) {
  3. List<Object> locations = new ArrayList<Object>();
  4. //获取Key分类
  5. String tip = getTip(tips);
  6. locations.add(tip);
  7. int length = tips.length;
  8. for (int i = 0; i < length; i++) {
  9. if (tip.equals(tips[i])) {
  10. locations.add(i+1);
  11. }
  12. }
  13. return locations;
  14. }
复制代码

选取了8张图片后,接下来就是合并图片。合并图片可以用到BufferedImage这个类的方法:setRGB()这个方法如果不明白可以看下api文档,上面有详细的说明。

  1. public static void mergeImage(File[] finalImages, HttpServletResponse response) throws IOException {
  2. //读取图片
  3. BufferedImage mergeImage = new BufferedImage(800, 400, BufferedImage.TYPE_INT_BGR);
  4. for (int i = 0; i < 8; i++) {
  5. File image = finalImages[i];
  6. BufferedImage bufferedImage = ImageIO.read(image);
  7. int width = bufferedImage.getWidth();
  8. int height = bufferedImage.getHeight();
  9. //从图片中读取RGB
  10. int[] imageBytes = new int[width*height];
  11. imageBytes = bufferedImage.getRGB(0, 0, width, height, imageBytes, 0, width);
  12. if ( i < 4) {
  13. mergeImage.setRGB(i*200, 0, width, height, imageBytes, 0, width);
  14. } else {
  15. mergeImage.setRGB((i -4 )*200, 200, width, height, imageBytes, 0, width);
  16. }
  17. }
  18. ImageIO.write(mergeImage, "jpg", response.getOutputStream());
  19. //ImageIO.write(mergeImage, "jpg", destImage);
  20. }
复制代码

  在controller层中,先把key分类保存到session中,为用户选择图片分类做提示和图片验证做判断。然后把图片流输出到response中,就可以生成验证图片了。

  1. response.setContentType("image/jpeg");
  2. response.setHeader("Pragma", "No-cache");
  3. response.setHeader("Cache-Control", "no-cache");
  4. response.setDateHeader("Expires", 0);
  5. List<Object> object = ImageSelectedHelper.getEightImages();
  6. File[] finalImages = (File[]) object.get(0);
  7. String[] tips = (String[]) object.get(1);
  8. //所有key的图片位置,即用户必须要选的图片
  9. List<Object> locations = ImageSelectedHelper.getLocation(tips);
  10. String tip = locations.get(0).toString();
  11. System.out.println(tip);
  12. session.setAttribute("tip", tip);
  13. locations.remove(0);
  14. int length = locations.size();
  15. for (int i = 0; i < length; i++) {
  16. System.out.println("实际Key图片位置:" + locations.get(i));
  17. }
  18. session.setAttribute("locations", locations);
  19. ImageMerge.mergeImage(finalImages, response);
复制代码

  在jsp中,为用户的点击生成小图片标记。当用户点图片击时,在父div上添加一个子div标签,并且把他定位为relative, 并且设置zIndex,然后再这个div上添加一个img标签,定位为absolute。在用户的点击时,可以获取点击事件,根据点击事件获取点击坐标,然后减去父div的坐标,就可以获取相对坐标。可以根据自己的喜好定坐标原点,这里的坐标原点是第8个图片的右下角。  

  1. <div><br> <div id="base"><br> <img src="<%=request.getContextPath()%>/identify" style="width: 300px; height: 150px;" onclick="clickImg(event)" id="bigPicture"><br> </div><br> <br> </div><br><br>function clickImg(e) {
  2. var baseDiv = document.getElementById("base");
  3. var topValue = 0;
  4. var leftValue = 0;
  5. var obj = baseDiv;
  6. while (obj) {
  7. leftValue += obj.offsetLeft;
  8. topValue +=obj.offsetTop;
  9. obj = obj.offsetParent;
  10. }
  11. //解决firefox获取不到点击事件的问题
  12. var clickEvent = e ? e : (window.event ? window.event : null);
  13. var clickLeft = clickEvent.clientX + document.body.scrollLeft - document.body.clientLeft - 10;
  14. var clickTop = clickEvent.clientY + document.body.scrollTop - document.body.clientTop - 10;
  15. var divId = "img_" + index++;
  16. var divEle = document.createElement("div");
  17. divEle.setAttribute("id", divId);
  18. divEle.style.position = "relative";
  19. divEle.style.zIndex = index;
  20. divEle.style.width = "20px";
  21. divEle.style.height = "20px";
  22. divEle.style.display = "inline";
  23. divEle.style.top = clickTop - topValue - 150 + 10 + "px";
  24. divEle.style.left = clickLeft - leftValue - 300 + "px";
  25. divEle.setAttribute("onclick", "remove('" + divId + "')");
  26. baseDiv.appendChild(divEle);
  27. var imgEle = document.createElement("img");
  28. imgEle.src = "<%=request.getContextPath()%>/resources/timo.png";
  29. imgEle.style.width = "20px";
  30. imgEle.style.height = "20px";
  31. imgEle.style.top = "0px";
  32. imgEle.style.left = "0px";
  33. imgEle.style.position = "absolute";
  34. imgEle.style.zIndex = index;
  35. divEle.appendChild(imgEle);
  36. }
复制代码

用户选择登录后,服务器端根据用户的选择坐标进行判断

  1. public List<Integer> isPass(String result) {
  2. String[] xyLocations = result.split(",");
  3. //保存用户选择的坐标落在哪些图片上
  4. List<Integer> list = new ArrayList<Integer>();
  5. //每一组坐标
  6. System.out.println("用户选择图片数:"+xyLocations.length);
  7. for (String xyLocation : xyLocations) {
  8. String[] xy = xyLocation.split("\|\|");
  9. int x = Integer.parseInt(xy[0]);
  10. int y = Integer.parseInt(xy[1]);
  11. //8,4图片区间
  12. if ( x > -75 && x <= 0) {
  13. if ( y > -75 && y <= 0) { //8号
  14. list.add(8);
  15. } else if ( y >= -150 && y <= -75 ) { //4号
  16. list.add(4);
  17. }
  18. } else if ( x > -150 && x <= -75) { //7,3图片区间
  19. if ( y > -75 && y <= 0) { //7号
  20. list.add(7);
  21. } else if ( y >= -150 && y <= -75 ) { //3号
  22. list.add(3);
  23. }
  24. } else if ( x > -225 && x <= -150) { //6,2图片区间
  25. if ( y > -75 && y <= 0) { //6号
  26. list.add(6);
  27. } else if ( y >= -150 && y <= -75 ) { //2号
  28. list.add(2);
  29. }
  30. } else if ( x >= -300 && x <= -225) { //5,1图片区间
  31. if ( y > -75 && y <= 0) { //5号
  32. list.add(5);
  33. } else if ( y >= -150 && y <= -75 ) { //1号
  34. list.add(1);
  35. }
  36. } else {
  37. return null;
  38. }
  39. }
  40. return list;
  41. }
复制代码

刷新生成新的图片,由于ajax不支持二进制流,可以自己用原生的xmlHttpRequest对象加html5的blob来完成。

  1. function refresh() {
  2. var url = "<%=request.getContextPath()%>/identify";
  3. var xhr = new XMLHttpRequest();
  4. xhr.open('GET', url, true);
  5. xhr.responseType = "blob";
  6. xhr.onload = function() {
  7. if (this.status == 200) {
  8. var blob = this.response;
  9. //加载成功后释放blob
  10. bigPicture.onload = function(e) {
  11. window.URL.revokeObjectURL(bigPicture.src);
  12. };
  13. bigPicture.src = window.URL.createObjectURL(blob);
  14. }
  15. }
  16. xhr.send();
复制代码

验证码整体代码完成了,还有有一些细节要处理。

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

最新评论

小黑屋|在路上 ( 蜀ICP备15035742号-1 

;

GMT+8, 2025-5-6 13:09

Copyright 2015-2025 djqfx

返回顶部